Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
31 Jul 2025 · 6 min read ·Article 31 / 125
Go

31 Integrating GraphQL with a PostgreSQL Database

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

31 Integrating GraphQL with a PostgreSQL Database

GraphQL and PostgreSQL are two fundamental technologies that are widely used in modern application development. GraphQL offers an efficient and flexible way to query data, while PostgreSQL, as one of the most powerful relational databases, provides a reliable foundation for data storage. So how do we go about integrating GraphQL with PostgreSQL from start to finish? This article walks through 31 essential integration points, complete with code, diagrams, tables, and insights from a professional engineer’s perspective.


1. Why GraphQL and PostgreSQL?

GraphQL is a query language for APIs developed by Facebook. It allows clients to request only the data they actually need.

PostgreSQL is a feature-rich, highly scalable open-source RDBMS that supports advanced queries. Combining the two delivers an API that is flexible, consistent, and high-performance.


2. General Architecture

MERMAID
graph TD
    Client -->|Request| GraphQL-Server
    GraphQL-Server -->|Query| PostgreSQL
    PostgreSQL -->|Response| GraphQL-Server
    GraphQL-Server -->|Response| Client

In the diagram above, the client sends a request to the GraphQL server. The GraphQL server converts that request into an SQL query for PostgreSQL, fetches the data, and returns the result through the GraphQL endpoint.


ToolLanguageStrengths
PrismaJS/TSModern ORM, migrations, type-safety
TypeORMJS/TSDecorators, flexible
HasuraHaskellInstant GraphQL API
PostGraphileJSAuto-generates GraphQL API
GraphenePythonORM ready, flexible
AriadnePythonSchema first, middleware
Apollo ServerJS/TSBroad integration, plugin-rich

4. Example Database Connection with Node.js

Use Node.js, express-graphql, and pg to get started. Here is an example of initializing the connection:

javascript
 1const { Pool } = require('pg');
 2const pool = new Pool({
 3    user: 'postgres',
 4    host: 'localhost',
 5    database: 'mydb',
 6    password: 'password',
 7    port: 5432,
 8});
 9
10// Example query
11async function getUsers() {
12    const res = await pool.query('SELECT * FROM users');
13    return res.rows;
14}

5. GraphQL Schema

Translate your PostgreSQL table structure into a GraphQL schema:

graphql
 1type User {
 2  id: ID!
 3  name: String!
 4  email: String!
 5  created_at: String!
 6}
 7
 8type Query {
 9  users: [User]
10  user(id: ID!): User
11}
12
13type Mutation {
14  addUser(name: String!, email: String!): User
15  updateUser(id: ID!, name: String, email: String): User
16  deleteUser(id: ID!): Boolean
17}

6. Resolvers

Connect the queries above to PostgreSQL using resolvers in Node.js:

javascript
 1const resolvers = {
 2  Query: {
 3    users: async () => await getUsers(),
 4    user: async (_, { id }) => {
 5      const res = await pool.query('SELECT * FROM users WHERE id = $1', [id]);
 6      return res.rows[0];
 7    }
 8  },
 9  Mutation: {
10    addUser: async (_, { name, email }) => {
11      const res = await pool.query(
12        'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *',
13        [name, email]
14      );
15      return res.rows[0];
16    }
17  }
18};

31 GraphQL + PostgreSQL Integrations

Here I summarize 31 aspects worth implementing for a robust architecture:

Database Schema & Migration

  1. Schema Introspection: Query table structure via GraphQL and auto-generate type definitions.
  2. Auto-migration: Tools like Prisma/TypeORM for schema synchronization.
  3. Custom Scalars: Map PostgreSQL types to GraphQL scalars (e.g., JSONB to GraphQLJSONObject).

Query & Filter

  1. Pagination: Implement LIMIT, OFFSET, or cursor-based (relay) pagination.
  2. Sorting: Query with ordering (ORDER BY).
  3. Filtering: Dynamically generate filter arguments in GraphQL.
  4. Full Text Search: Integrate with PostgreSQL FTS (to_tsvector).
  5. Aggregation: Query sum, avg, min, max through resolvers.

Complex Relationships

  1. One-to-Many: Map foreign key relationships.
  2. Many-to-Many: Through/junction tables in GraphQL.
  3. Nested Query: Resolve parent-child relationships (e.g., post - comments).
  4. Batching: Use DataLoader to avoid N+1 queries.

Mutations

  1. Transactional Mutation: Run mutations inside a transaction block.
  2. Upsert: Use ON CONFLICT in SQL and expose it in mutations.
  3. Bulk Insert/Update: Mutate multiple records in a single operation.
  4. Soft Delete: Use deleted_at instead of a hard delete.
  5. Audit Trail: Persist changes to log history.

Security

  1. Role-based Authorization: Control access per GraphQL resolver.
  2. Row Level Security: A native PostgreSQL feature for multi-tenancy.
  3. Input Validation: Validate GraphQL input before it reaches SQL.

Performance

  1. Query Optimization: Select fields; fetch only what the client requests.
  2. Connection Pooling: Avoid connection bottlenecks.
  3. Indexing: Add indexes on frequently queried fields.
  4. Caching: DataLoader or query result caching.

Monitoring

  1. Query Logging: Track slow queries and errors.
  2. Tracing: Integrate OpenTelemetry for tracing.

Advanced Integration

  1. Subscriptions (Real-time): Database trigger → pub/sub in GraphQL, for example using pg_notify.
  2. Function Exposure: Expose PostgreSQL functions as GraphQL mutations/queries.
  3. Materialized Views: Present aggregated/summary data as GraphQL types.
  4. JSONB & Array: Expose advanced PostgreSQL types to GraphQL.
  5. Temporal Tables: Use temporal features for data history in GraphQL.

Case Study: Querying a “User-Post” Relationship with Pagination

  1. PostgreSQL Schema:
sql
 1CREATE TABLE users (
 2  id SERIAL PRIMARY KEY,
 3  name TEXT NOT NULL
 4);
 5
 6CREATE TABLE posts (
 7  id SERIAL PRIMARY KEY,
 8  user_id INTEGER REFERENCES users(id),
 9  title TEXT NOT NULL,
10  created_at TIMESTAMP NOT NULL DEFAULT NOW()
11);
  1. GraphQL Schema:
graphql
 1type User {
 2  id: ID!
 3  name: String!
 4  posts(limit: Int = 10, offset: Int = 0): [Post!]!
 5}
 6
 7type Post {
 8  id: ID!
 9  user: User!
10  title: String!
11  created_at: String!
12}
  1. Resolver (Node.js):
javascript
 1const resolvers = {
 2  User: {
 3    posts: async (parent, { limit, offset }) => {
 4      const res = await pool.query(
 5        'SELECT * FROM posts WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3',
 6        [parent.id, limit, offset]
 7      );
 8      return res.rows;
 9    }
10  },
11  Post: {
12    user: async (parent) => {
13      const res = await pool.query('SELECT * FROM users WHERE id = $1', [parent.user_id]);
14      return res.rows[0];
15    }
16  }
17};

Mapping Diagram Table: Feature Mapping

GraphQL FeaturePostgreSQLNotes
type, inputTable/TypeMaps entities
field resolverSELECT, JOINFetch relations, select columns
mutationINSERT, UPDATE, DELETEMutate data
subscriptionLISTEN/NOTIFYReal-time events
scalarBasic/Custom TypeUUID, JSON, ARRAY, etc

Conclusion

Integrating GraphQL and PostgreSQL is an interesting journey full of both challenges and strengths. By following the 31 integration practices above, you can build a GraphQL API on top of PostgreSQL that is flexible, secure, and scalable. Whether you use Hasura, PostGraphile, Prisma, or custom resolvers in Node.js or Python, the principles above remain relevant.

Finally, remember to always measure and optimize your implementation, because real-world production needs are far more complex than a simple “Hello World.” GraphQL and PostgreSQL are “power tools,” and with a deep understanding, we can unlock their full potential.


Further reading:

Happy experimenting and building a resilient API! 🚀

Related Articles

💬 Comments