What Is a Database API? REST, GraphQL, and the SQL Data Layer Explained
A database API is a software layer that sits between client applications and a SQL database, translating HTTP requests into SQL queries and returning structured responses, typically as JSON. Rather than granting application code a direct connection to the database with raw SQL access, the API layer mediates every interaction: it authenticates the caller, authorizes the requested operation, generates or routes the appropriate SQL, executes it against a managed connection pool, and serializes the result set into an HTTP response. This pattern decouples clients from the physical schema, centralizes access control, and makes the database accessible to any language or platform that can issue an HTTP request. By the end of this article, you will understand exactly how database APIs work, where they sit in the application stack, how REST and GraphQL each approach the problem differently, and why direct database connections from application code become a liability at scale.
Key Takeaways
- A database API translates HTTP requests into SQL queries, giving clients structured data access without direct database connections.
- The API layer centralizes authentication, authorization, connection pooling, and query generation in a single, auditable process.
- REST maps SQL tables to resource endpoints with fixed response shapes; GraphQL exposes a schema that lets clients request exactly the fields and relationships they need.
- Direct database connections from application code create security, scaling, and operational problems that an API layer is specifically designed to solve.
- Connection pooling inside the API layer reduces the number of active database connections from potentially thousands to a small, managed set.
- gRPC is a valid transport for internal service-to-service database access but lacks the browser support and ad-hoc tooling that REST and GraphQL provide.
What Problem Does a Database API Solve?
Every SQL database speaks a wire protocol. PostgreSQL uses its own binary protocol on port 5432. MySQL uses its protocol on 3306. SQL Server uses TDS on 1433. To query any of these databases, a client must open a TCP connection, authenticate with database credentials, send SQL text, and parse the binary result set. This works well when a single monolithic application owns the database. It does not work well when dozens of services, front-end applications, mobile clients, and third-party integrations all need access to the same data.
The fundamental problem is one of mediation. Without an API layer, every client that needs data must hold database credentials, maintain its own connection pool, construct its own SQL, and implement its own authorization logic. The database itself has limited tools for enforcing fine-grained access control; its permission model operates at the level of tables and columns, not at the level of business rules like “a user can only read orders that belong to their own organization.” A database API solves this by consolidating all data access into a single HTTP-accessible service that enforces consistent rules regardless of which client is calling.
Direct Database Access vs. an API Layer
To understand the value of a database API, it helps to compare the two approaches concretely. In a direct-access architecture, a front-end application or microservice holds a connection string containing the database host, port, username, and password. The application opens a connection (or draws one from a local pool), sends a SQL statement like SELECT * FROM orders WHERE customer_id = 42, receives a result set, and closes or returns the connection.
This pattern introduces several compounding problems. First, credentials must be distributed to every service that needs data, increasing the attack surface proportionally. Second, every service must independently implement connection pooling, which is the practice of maintaining a set of pre-established database connections that are reused across requests to avoid the overhead of opening and closing connections for each query. If one poorly written service leaks connections, it can exhaust the database’s connection limit and cause an outage that affects every other service. Third, there is no centralized audit log of who accessed what data and when. Fourth, schema changes in the database propagate unpredictably to every consuming service.
An API layer eliminates all four problems. Credentials live in one place. Connection pooling is managed by one process. Access logs flow through a single chokepoint. Schema changes can be absorbed by the API layer without immediately breaking downstream clients. For a deeper treatment of how this pattern works with REST specifically, see the complete guide to REST APIs for SQL databases.
REST, GraphQL, and gRPC from a SQL Database Perspective
REST: Resource-Oriented Endpoints
REST (Representational State Transfer) is an architectural style that maps HTTP methods to CRUD operations on resources. In the context of a database API, each significant table or view typically becomes a resource with its own URL path. A customers table becomes a /customers endpoint. A GET request to /customers/42 translates into a SQL query like SELECT * FROM customers WHERE id = 42. A POST to /customers with a JSON body becomes an INSERT. A PUT or PATCH becomes an UPDATE. A DELETE becomes a DELETE.
The strength of REST for database APIs is its predictability. Each endpoint has a fixed response shape determined by the underlying table’s columns. HTTP caching works out of the box because every resource has a stable URL. Rate limiting, authentication, and monitoring can all be implemented at the HTTP layer using standard middleware and proxies. The weakness is that REST endpoints return fixed response shapes. A client that needs only three columns out of twenty still receives all twenty, a problem known as over-fetching. A client that needs data from three related tables must make three separate requests, a problem known as under-fetching, unless the API designer has created a custom composite endpoint.
GraphQL: Client-Defined Queries
GraphQL takes a fundamentally different approach. Instead of multiple resource endpoints, a GraphQL database API exposes a single endpoint (typically /graphql) with a typed schema that describes every available entity, field, and relationship. The client sends a query specifying exactly which fields it wants and how deep into related entities it needs to traverse. The server parses that query, generates the corresponding SQL (often including JOINs or subqueries to resolve relationships), executes it, and returns a JSON response that matches the shape of the query.
For SQL database APIs, GraphQL’s advantage is precision. A mobile client that needs only a customer’s name and email sends a query requesting only those two fields, and the server generates SELECT name, email FROM customers WHERE id = 42 rather than selecting every column. A dashboard that needs orders with their line items and product names sends a single nested query that the server resolves through JOINs, eliminating the multiple round trips that REST would require.
The cost is complexity. GraphQL queries are unbounded by default: a client can request deeply nested relationships that translate into expensive multi-table JOINs or even recursive queries. Without query depth limiting and cost analysis (mechanisms that assign a computational cost to each field and reject queries that exceed a threshold), a single malicious or careless query can bring the database to its knees. The server must also solve the N+1 query problem, where resolving a list of parent entities triggers a separate SQL query for each parent’s related children, by implementing batching strategies such as DataLoader. For a detailed comparison of these tradeoffs, see the analysis of GraphQL vs. REST for database APIs.
gRPC: Binary Protocol for Internal Services
gRPC uses HTTP/2 as its transport and Protocol Buffers (a binary serialization format developed by Google) as its interface definition language. A database API built on gRPC defines its service contract in a .proto file, which is then compiled into client and server stubs in any supported language. The binary serialization is more compact and faster to parse than JSON. HTTP/2’s multiplexing allows multiple concurrent requests over a single TCP connection.
In practice, gRPC database APIs are most common in internal service-to-service communication where both the caller and the server are backend services running in the same data center or Kubernetes cluster. gRPC is less common at the edge because browsers cannot natively call gRPC services without a gRPC-Web proxy, and operational tooling for debugging and exploring gRPC services is less mature than the equivalent for REST or GraphQL. Most teams that expose SQL data via gRPC also run a REST or GraphQL gateway in front of it for external consumers.
Where the API Layer Sits in the Application Stack
In a typical web application, the stack has four tiers: the client (browser, mobile app, or another service), the API layer, the application logic layer, and the database. The API layer sits immediately in front of the database, behind any application-specific business logic. In some architectures, the API layer and the business logic layer are the same process, a pattern common in monolithic frameworks like Rails or Django. In others, the API layer is a distinct service that exposes the database as a generic data source, and separate application services consume it.
The key architectural property is that the API layer is the only process that holds database credentials and opens database connections. Every other component in the system accesses data through HTTP calls to the API layer. This creates a single point of observability (all queries flow through one process), a single point of access control (all authorization checks happen in one place), and a single point of connection management (the database sees one pool of connections rather than a separate pool from each consuming service).
This architecture has a direct relationship to how database API security is implemented. Because the API layer is the sole gateway to the data, every security control, from TLS termination to role-based row filtering, can be enforced at this single chokepoint.
Why Direct Connections Create Scaling and Security Problems
The scaling problem with direct database connections is arithmetic. A typical PostgreSQL instance defaults to a maximum of 100 simultaneous connections. Each connection consumes roughly 10 MB of memory on the server. If you have 20 microservices, each running 10 instances, each maintaining a connection pool of 5 connections, you need 1,000 connections, ten times the default limit. Raising max_connections works up to a point, but each additional connection degrades performance because PostgreSQL uses a process-per-connection model where the operating system must context-switch between hundreds of processes.
A database API solves this through connection pooling and multiplexing. The API layer maintains a small pool of connections, often 20 to 50, and multiplexes thousands of concurrent HTTP requests across that pool. A client’s HTTP request waits in a queue for a connection to become available, executes its query, and returns the connection to the pool. External tools like PgBouncer serve a similar purpose for PostgreSQL specifically, but an API layer provides pooling as a built-in feature alongside authentication and query generation.
The security problem is equally concrete. Database credentials embedded in application code or environment variables are a high-value target. If an attacker compromises any single service that holds database credentials, they gain direct SQL access to the database, including the ability to run arbitrary queries, extract bulk data, or modify records in ways that bypass all application-level authorization logic. An API layer reduces the blast radius: even if a consuming service is compromised, the attacker obtains only an API token with scoped permissions, not raw database access. The API layer continues to enforce row-level filtering, column-level redaction, and rate limits on every request.
Connection Pooling, Query Abstraction, and Access Control
Connection Pooling
Connection pooling is the mechanism by which the API layer maintains a set of pre-established TCP connections to the database and lends them out to incoming requests. When a request arrives, the API process checks a connection out of the pool, executes the SQL, and checks it back in. This eliminates the per-request cost of TCP handshake, TLS negotiation (if the database connection uses TLS), and authentication, which collectively can add 20 to 50 milliseconds per connection on a remote database. The pool is configured with a minimum size (the number of connections kept open at idle), a maximum size (the hard upper bound), and an idle timeout (how long an unused connection remains open before being closed). Tuning these parameters is essential to balancing resource utilization against request latency.
Query Abstraction
Query abstraction refers to the translation of an incoming API request into a SQL statement. In a REST database API, this translation is typically deterministic: GET /customers?status=active&sort=created_at&limit=50 becomes SELECT * FROM customers WHERE status = 'active' ORDER BY created_at LIMIT 50. In a GraphQL database API, the translation is more complex because the query can span relationships and request arbitrary combinations of fields. The API layer uses a query builder, which is an internal component that programmatically constructs SQL from the parsed request, handling parameterization (using prepared statement placeholders like $1 instead of string interpolation to prevent SQL injection), escaping, and dialect-specific syntax differences between PostgreSQL, MySQL, SQL Server, and other engines.
The abstraction layer also provides an insertion point for query modification. Row-level security policies, for example, can be implemented by appending WHERE tenant_id = $current_tenant to every generated query, ensuring that a client authenticated as Tenant A can never read Tenant B’s data regardless of what the client requests.
Access Control
Access control in a database API operates at a higher level of abstraction than database-native permissions. Where a database grants or revokes access to entire tables or columns, an API layer can enforce rules like: this API key can read the customers table but only rows where region = 'EU', only the name, email, and created_at columns, and only at a rate of 100 requests per minute. These rules are expressed in the API layer’s configuration or code, not in the database’s permission system. The API layer validates the caller’s identity (through API keys, OAuth 2.0 bearer tokens, or JWTs, which are JSON Web Tokens containing cryptographically signed claims about the caller’s identity and permissions), checks the caller’s authorization against the configured rules, and either proceeds with the query or returns an HTTP 403 Forbidden response.
Choosing the Right Database API Pattern
The decision between REST and GraphQL for a database API is not purely technical; it depends on who the consumers are and how they access data. REST is the safer default when the API serves external consumers, when HTTP caching is important, when the access patterns are well-understood and relatively uniform, or when the team prioritizes operational simplicity. GraphQL is the stronger choice when the API serves multiple front-end clients with different data needs (a common scenario in mobile-plus-web applications), when minimizing payload size and round trips matters for performance, or when the data model involves heavily interconnected entities that clients need to traverse flexibly.
Some teams run both: a GraphQL API for internal front-end consumption and a REST API for external integrations. This is a pragmatic approach that plays to each protocol’s strengths, though it doubles the surface area that must be maintained and secured. Regardless of protocol, the underlying principles of connection pooling, query parameterization, and centralized access control apply identically. The protocol is the interface; the API layer’s value lies in the mediation it provides between untrusted clients and the database.
For teams evaluating specific database engines, the choice of API pattern may also be influenced by the database’s native capabilities and the tooling available for that platform. The tradeoffs can differ meaningfully between, for example, PostgreSQL’s rich type system and JSON support and the constraints of other engines.
Frequently Asked Questions
When should I use REST instead of GraphQL for a database API?
REST is the stronger choice when your API consumers expect stable, cacheable endpoints with predictable response shapes. If your SQL database backs a public API that third parties integrate against, REST’s resource-oriented contract makes versioning straightforward and lets HTTP caching layers like Varnish or CDN edge nodes cache responses by URL without any custom logic. REST also tends to be simpler operationally: every endpoint maps to a known query pattern, which makes performance profiling and query optimization easier. Choose REST when the access patterns are well-known in advance, when HTTP-level caching matters, or when your team’s operational tooling is built around request-per-endpoint observability.
Does a database API replace an ORM like SQLAlchemy or Hibernate?
A database API and an ORM solve different problems at different layers of the stack. An ORM maps database rows to in-process objects inside a single application runtime. It handles identity mapping, lazy loading, change tracking, and unit-of-work patterns within that process. A database API, by contrast, exposes data over HTTP so that any authorized client, regardless of language or runtime, can read and write records without a direct database connection. In architectures where multiple services or front-end applications need the same data, a database API provides a shared, protocol-level interface that an ORM cannot. That said, many database API frameworks use an ORM internally to generate SQL. The two are complementary rather than competing: the ORM operates inside the API layer, and the API layer operates between the client and the database.
What is the latency overhead of putting an API layer in front of my SQL database?
The latency a database API introduces depends on the implementation, but for a well-built layer running in the same network as the database, the overhead is typically between 1 and 5 milliseconds per request. That figure covers HTTP parsing, authentication token validation, query generation, and response serialization. Connection pooling eliminates the TCP and TLS handshake cost that would otherwise dominate per-request latency. In practice, this overhead is negligible compared to the query execution time for any non-trivial SQL statement. The tradeoff is favorable because the API layer also enables connection multiplexing, which means a database that would otherwise be overwhelmed by thousands of direct client connections can serve the same load through a small, reusable pool of connections managed by the API process.
Can I use gRPC instead of REST or GraphQL for a database API?
gRPC is a viable transport for database APIs, particularly in service-to-service communication within a microservices architecture. It uses HTTP/2 for multiplexed streams, Protocol Buffers for compact binary serialization, and generated client stubs for type-safe calls. These properties make gRPC faster on the wire than JSON-based REST for high-throughput internal traffic. However, gRPC has meaningful limitations for database APIs that face external consumers: browser support requires a gRPC-Web proxy, tooling for ad-hoc exploration is weaker than REST or GraphQL, and the Protocol Buffer schema evolution model is more rigid than JSON’s flexible structure. Most teams that use gRPC for database access do so behind an API gateway that translates gRPC to REST or GraphQL at the edge.
How does a database API handle transactions that span multiple tables?
Transaction support in database APIs varies by implementation. Simple auto-generated CRUD APIs typically wrap each request in its own database transaction: a single POST, PUT, or DELETE executes within one transaction and either fully commits or fully rolls back. For operations that must atomically modify multiple tables, the API layer generally offers one of two approaches. The first is a stored procedure or server-side function endpoint, where the client calls a single API endpoint that triggers a stored procedure containing the multi-statement transaction logic. The second is a batch or transaction endpoint that accepts an array of operations and executes them within a single database transaction, rolling back all of them if any individual operation fails. The batch approach is more flexible but requires the API framework to support it explicitly.
What happens to my database API when the underlying schema changes?
Schema changes are one of the most operationally significant events for a database API. If the API layer auto-generates endpoints from the database schema, adding a column is usually non-breaking because existing clients simply ignore the new field. Removing or renaming a column, however, will break any client that references it. The standard mitigation strategies are API versioning and schema migration coordination. With versioning, the old API version continues to serve the previous response shape while the new version reflects the updated schema. With migration coordination, the team applies database migrations and API changes together in a controlled deployment, often using feature flags or canary releases to limit blast radius. API layers that use explicit endpoint definitions rather than auto-generation give the team full control over when and how schema changes propagate to consumers.