tds-nio

0.1.0-alpha.1

Non-blocking, event-driven Swift implementation of the TDS Protocol
aaronjedwards/tds-nio

What's New

0.1.0-alpha.1

2026-06-01T21:30:16Z

Initial Pre-Release Version

This project is very early in development and is PRE-RELEASE software. It is not ready for production use cases.

Still rounding out

  • project structure
  • TDS protocol completness
  • etc.

TDSNIO

Supported Swift Versions Supported Platforms Documentation Apache 2.0 License CI

Non-blocking, event-driven Swift client for Microsoft SQL Server built on SwiftNIO.

TDSNIO is a client implementation of the Tabular Data Stream (TDS) protocol, with APIs for connecting to, authenticating with, querying, and pooling connections to SQL Server.

TDSNIO is currently pre-release software and public APIs may change substantially before a stable release.

Features

  • A TDSConnection for connecting to SQL Server, running SQL batches, and reading results.
  • Async/await APIs for queries, row streams, and connection pooling.
  • Swift string interpolation for parameterized SQL through sp_executesql.
  • Automatic conversions between common Swift/Foundation types and SQL Server values.
  • Integrated logging with swift-log.
  • Support for TLS negotiation using swift-nio-ssl.
  • Support for Network.framework transports when available on Apple platforms.
  • A TDSClient connection pool backed by PostgresNIO's _ConnectionPoolModule.

Supported SQL Server Versions

TDSNIO targets TDS 7.4 and TDS 8.0 capable SQL Server deployments.

Version Tested
SQL Server 2019 Not yet part of CI
SQL Server 2022 Yes, in GitHub Actions
Azure SQL Database Not yet part of CI

The current CI suite runs integration tests against mcr.microsoft.com/mssql/server:2022-latest.

Language and Platform Support

TDSNIO currently requires Swift 6.1 or newer.

Getting Started

Adding the Dependency

dependencies: [
    .package(url: "https://github.com/aaronjedwards/tds-nio.git", exact: "0.1.0-alpha.1"),
]

Add TDSNIO to the target that uses it:

targets: [
    .target(name: "MyTarget", dependencies: [
        .product(name: "TDSNIO", package: "tds-nio"),
    ])
]

Creating a Connection

Create a TDSConnection.Configuration with the SQL Server host, credentials, and optional initial database:

import Logging
import TDSNIO

let configuration = TDSConnection.Configuration(
    host: "127.0.0.1",
    port: 1433,
    username: "sa",
    password: "your-strong-password",
    database: "app_database"
)

let logger = Logger(label: "tds-nio")

let connection = try await TDSConnection.connect(
    configuration: configuration,
    id: 1,
    logger: logger
)

try await connection.close()

You can also build the same configuration from a SQL Server-style connection string:

let configuration = try TDSConnection.Configuration(
    connectionString: "Server=127.0.0.1,1433;User Id=sa;Password=your-strong-password;Database=app_database"
)

All connections can use TDSConnection.Configuration.TLS. Use .prefer(_:) or .require(_:) with a NIOSSLContext when TLS should be negotiated:

import NIOSSL
import TDSNIO

let sslContext = try NIOSSLContext(configuration: .makeClientConfiguration())

let configuration = TDSConnection.Configuration(
    host: "sql.example.com",
    username: "sa",
    password: "your-strong-password",
    database: "app_database",
    tls: .require(sslContext)
)

Running SQL Statements

Use query(_:) when you want rows from the first result set:

let rows = try await connection.query("""
    SELECT id, username, created_at
    FROM users
    ORDER BY id
    """)

for try await row in rows {
    let id = try row.decode(column: "id", as: Int.self)
    let username = try row.decode(column: "username", as: String.self)
    let createdAt = try row.decode(column: "created_at", as: Date.self)

    print(id, username, createdAt)
}

Rows can also be decoded positionally:

let rows = try await connection.query("SELECT id, username FROM users")

for try await (id, username) in rows.decode((Int, String).self) {
    print(id, username)
}

Use execute(_:) when you need the full TDSQueryResult, including rows affected, output parameters, return status, or additional result-set metadata:

let result = try await connection.execute("""
    UPDATE users
    SET is_active = 1
    WHERE last_login_at IS NOT NULL
    """)

print(result.rowsAffected ?? 0)

Statements With Parameters

TDSQuery supports Swift string interpolation for bind parameters. Interpolated values are sent as parameters through sp_executesql, rather than being pasted into the SQL string:

let id = 42
let username = "fancyuser"

try await connection.execute("""
    INSERT INTO users (id, username)
    VALUES (\(id), \(username))
    """)

Optional values can be interpolated as well:

let lastLoginAt: Date? = nil

try await connection.execute("""
    UPDATE users
    SET last_login_at = \(lastLoginAt)
    WHERE id = \(id)
    """)

The following common Swift and Foundation types can be bound into queries and decoded from rows:

  • Bool
  • UInt8, Int16, Int32, Int, Int64
  • Float, Double, Decimal
  • String
  • [UInt8], ByteBuffer, Data
  • UUID, TDSGUID
  • Date, TDSDate, TDSTime, TDSDateTime, TDSDateTimeOffset
  • TDSJSON, TDSJSONValue
  • TDSTableValuedParameter

Connection Pooling

For applications that need to reuse connections, create a TDSClient from a connection configuration and run it in a long-lived task:

import Logging
import TDSNIO

var options = TDSClient.Options()
options.maximumConnections = 10

let client = TDSClient(
    configuration: configuration,
    options: options,
    backgroundLogger: Logger(label: "tds-nio.pool")
)

Task {
    await client.run()
}

try await client.withConnection { connection in
    let rows = try await connection.query("SELECT id, username FROM users")

    for try await (id, username) in rows.decode((Int, String).self) {
        print(id, username)
    }
}

withConnection(_:) leases a connection for the closure's lifetime and returns it to the pool afterward. The connection is marked for reset before its next request so pooled sessions do not accidentally share session state between callers.

Changelog

Pre-release changes will be documented on the GitHub releases page once tags exist.

License

Apache 2.0

Copyright (c) 2026 Aaron Edwards and TDSNIO project authors.

This project contains code and design work influenced by other open source projects. See NOTICE.txt for attribution details.

Microsoft SQL Server is a trademark of Microsoft Corporation. Any use of the trademark is for identification only and does not imply affiliation with or endorsement by Microsoft.

Swift is a trademark of Apple Inc. Any use of the trademark is for identification only and does not imply affiliation with or endorsement by Apple.

Description

  • Swift Tools 6.1.0
View More Packages from this Author

Dependencies

Last updated: Sun Jun 14 2026 05:42:59 GMT-0900 (Hawaii-Aleutian Daylight Time)