This guide demonstrates how to quickly build a fully functional, type-safe Node.js API using Prisma, typegraphql-prisma, and graphql-query-purifier. We'll create a full CRUD API with partial access to protect sensitive data, even with autogenerated resolvers.
Full example is available in repo
1. Project Setup and Prisma Integration
First, create a new Node.js project and integrate Prisma for database management.
Code for Project Setup:
mkdir nodejs-api && cd nodejs-api
npm init -y
npm i --save-dev @types/cors@2.8.16 @types/graphql-fields@1.3.9 @types/node@20.9.2 body-parser@1.20.2 cors@2.8.5 express@4.18.2 graphql-query-purifier prisma@5.6.0 ts-node@10.9.1 type-graphql@2.0.0-beta.1 typegraphql-prisma@0.27.1 typescript@5.2.2
npm i @apollo/server@4.9.5 @prisma/client@5.6.0 graphql@16.8.1 graphql-fields@2.0.3 graphql-scalars@1.22.4 reflect-metadata@0.1.13
npx tsc --init
npx prisma init --datasource-provider sqlite
2. Defining Prisma Models
Create a Prisma schema with models representing a company structure, including sensitive salary data.
schema.prisma:
// schema.prisma
datasource db {
provider = "sqlite"
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
generator typegraphql {
provider = "typegraphql-prisma"
output = "generated"
}
model Employee {
id Int @id @default(autoincrement())
name String
departmentId Int
department Department @relation(fields: [departmentId], references: [id])
salary Salary?
salaryId Int?
}
model Department {
id Int @id @default(autoincrement())
name String
employees Employee[]
}
model Salary {
id Int @id @default(autoincrement())
amount Float
employeeId Int @unique
employee Employee @relation(fields: [employeeId], references: [id])
}
Run migrations to create the database:
npx prisma migrate dev --name init
- Setting up Apollo Server with Express Integrate Apollo Server with Express, using TypeGraphQL for schema generation and resolvers.
Code for Server Setup:
import "reflect-metadata";
import express from "express";
import { ApolloServer } from "@apollo/server";
import { GraphQLQueryPurifier } from "graphql-query-purifier";
import { resolvers } from "../prisma/generated";
import { PrismaClient } from "@prisma/client";
import cors from "cors";
import path from "path";
import { json, urlencoded } from "body-parser";
import { expressMiddleware } from "@apollo/server/express4";
import { buildSchema } from "type-graphql";
const startServer = async () => {
const app = express();
const prisma = new PrismaClient();
app.use(cors(), json(), urlencoded({ extended: true }));
const gqlPath = path.resolve(__dirname, "../frontend");
const queryPurifier = new GraphQLQueryPurifier({
gqlPath,
allowStudio: true,
// allowAll: false,
});
app.use(queryPurifier.filter);
const server = new ApolloServer({
schema: await buildSchema({
resolvers,
validate: false,
}),
});
await server.start();
const context = expressMiddleware(server, {
context: async (_ctx) => ({
prisma,
}),
});
app.use("/graphql", context);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}/graphql`);
});
};
startServer();
- Testing the API Finally, let's test the API using GraphQL queries.
Legitimate Query:
# Fetch departments and their employees
query {
departments {
id
name
employees {
id
name
}
}
}
Malicious Query Attempt:
# Unauthorized attempt to access salaries
query {
departments {
id
name
employees {
id
name
salary {
amount
}
}
}
}
As you might've guessed - this query will show only data that is explicitly allowed to be shown by our .gql
files.
Conclusion
This guide covered creating a Node.js API with TypeGraphQL, graphql-query-purifier, and architecture. The main takeaways:
TypeGraphQL for Auto-Generated Schema and Resolvers:
- Efficiency: TypeGraphQL and Prisma produce database schema-based GraphQL resolvers. This accelerates the building of a GraphQL API with full CRUD capabilities.
- Type Safety: Strong API typing. A type-safe environment with TypeGraphQL and TypeScript reduces type-related problems and improves code quality.
Improved Security: graphql-query-purifier
GraphQL query-purifier filters incoming queries to prevent data leaks. Avoiding over-fetching and unauthorized access to sensitive data is crucial for APIs with autogenerated resolvers.
Our API protects sensitive data like employee pay. The graphql-query-purifier protects such data from unauthorized queries, which is crucial for compliance and privacy.
Practical Results:
Combining these tools creates a robust, secure, and scalable API. It handles complicated data structures and relationships securely.
Less Development Time and Effort: GraphQL schema and resolver auto-generation and security features reduce boilerplate code and increase business logic emphasis.
Modularity and cutting-edge tools prepare the API for future scalability and maintainability issues.
This API development method speeds up creation and adds security and type safety. It shows a fast and secure way to build modern web apps.
Top comments (0)