Backend Engineer Code Test — GraphQL API Server built with NestJS (TypeScript) and Apollo Server.
- Language: Node.js (>= 16) + TypeScript
- Framework: NestJS 11
- GraphQL: @nestjs/graphql 13 + Apollo Driver (@apollo/server 5)
- Config: @nestjs/config (
.env)
- Implements the provided GraphQL schema (
Action,Trigger,Response,ResourceTemplate,NodeObject) in schema-first mode - Uses the provided JSON files (
action.json,trigger.json,response.json,node.json,resourceTemplate.json) as the data source - Resolves cross-references between entities:
NodeObject.trigger/triggerIdNodeObject.responses/responseIdsNodeObject.actions/actionIds(mapped frompostActionsin the raw data)NodeObject.parents/parentIds(mapped from composite IDs)Action.resourceTemplate/Trigger.resourceTemplate
- Authenticated via Bearer token (HTTP 401
UNAUTHENTICATEDwhen missing/invalid)- Express middleware rejects invalid requests at the HTTP layer
- Global
AuthGuardprovides GraphQL-layer defense in depth
- Custom
LongandJSONscalars
npm install
npm run start:dev # watch mode
# or
npm run build && npm startServer will run at http://localhost:4000/graphql.
GraphQL Playground / Apollo Studio Sandbox requires the Authorization header. Use a tool like Postman, or the Apollo Studio Sandbox with the header
Authorization: Bearer <token>.
Send the following header with every request:
Authorization: Bearer code-test-token-2026
The token is configurable via the AUTH_TOKEN environment variable (see .env).
query {
node(nodeId: "6297172e70a0c165b989cd10") {
_id
name
parentIds
parents {
_id
name
compositeId
}
trigger {
_id
name
resourceTemplate {
_id
name
key
}
}
responses {
_id
name
platforms {
integrationId
build
localeGroups {
localeGroupId
variations {
name
responses
}
}
}
}
actions {
_id
name
resourceTemplate {
_id
name
}
}
}
}curl -X POST http://localhost:4000/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer code-test-token-2026" \
-d '{"query":"{ node(nodeId: \"6296be3470a0c1052f89cccb\") { _id name } }"}'graphql-api-nest/
├── data/ # JSON data source files
│ ├── action.json
│ ├── node.json
│ ├── resourceTemplate.json
│ ├── response.json
│ └── trigger.json
├── src/
│ ├── main.ts # Bootstrap + Bearer token middleware
│ ├── app.module.ts # GraphQLModule (ApolloDriver) + ConfigModule + global AuthGuard
│ ├── common/
│ │ └── auth/
│ │ └── auth.guard.ts # GraphQL-aware global guard (401 UNAUTHENTICATED)
│ ├── data/
│ │ ├── data.module.ts # @Global data module
│ │ ├── data.service.ts # JSON loading + _id/compositeId indexing
│ │ └── types.ts # Entity interfaces
│ └── graphql/
│ ├── typeDefs.ts # Provided GraphQL schema (SDL)
│ ├── scalars/
│ │ ├── long.scalar.ts # Long custom scalar
│ │ └── json.scalar.ts # JSON custom scalar
│ └── node/
│ ├── node.resolver.ts # @Resolver('NodeObject') query + field resolvers
│ └── related.resolvers.ts # Action/Trigger/Response/ResourceTemplate resolvers
├── .env # AUTH_TOKEN and PORT configuration
├── .gitignore
├── nest-cli.json
├── tsconfig.json
└── package.json
The raw JSON files use slightly different field names than the provided schema. The resolvers map them as follows:
| Schema field | Source field(s) |
|---|---|
NodeObject.triggerId / trigger |
trigger (ID) |
NodeObject.responseIds / responses |
responses (array of IDs) |
NodeObject.actionIds / actions |
postActions (array of IDs) |
NodeObject.parentIds / parents |
parents (array of composite IDs, resolved via compositeId) |
ResponseLocaleGroup.localeGroupId |
localeGroup |
ResourceTemplate.createdAt |
falls back to 0 when missing |