Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce complexity of extend-express-app example #9068

Merged
merged 2 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/custom-session/keystone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const sillySessionStrategy = {
export default config<TypeInfo>({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./keystone-example.db',
url: process.env.DATABASE_URL ?? 'file:./keystone-example.db',

// WARNING: this is only needed for our monorepo examples, dont do this
...fixPrismaPath,
Expand Down
87 changes: 64 additions & 23 deletions examples/extend-express-app/keystone.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
import { config } from '@keystone-6/core'
import type { Request, Response } from 'express'

import { fixPrismaPath } from '../example-utils'
import { lists } from './schema'
import { getTasks } from './routes/tasks'
import { type TypeInfo, type Context } from '.keystone/types'

function withContext<F extends (req: Request, res: Response, context: Context) => void>(
commonContext: Context,
f: F
) {
return async (req: Request, res: Response) => {
return f(req, res, await commonContext.withRequest(req, res))
}
}
import {
type TypeInfo,
} from '.keystone/types'

// WARNING: this example is for demonstration purposes only
// as with each of our examples, it has not been vetted
// or tested for any particular usage

export default config<TypeInfo>({
db: {
Expand All @@ -24,18 +19,64 @@ export default config<TypeInfo>({
...fixPrismaPath,
},
server: {
/*
This is the main part of this example. Here we include a function that
takes the express app Keystone created, and does two things:
- Adds a middleware function that will run on requests matching our REST
API routes, to get a keystone context on `req`. This means we don't
need to put our route handlers in a closure and repeat it for each.
- Adds a GET handler for tasks, which will query for tasks in the
Keystone schema and return the results as JSON
*/
extendExpressApp: (app, commonContext) => {
app.get('/rest/tasks', withContext(commonContext, getTasks))
// app.put('/rest/tasks', withContext(commonContext, putTask));
// this example HTTP GET handler retrieves any posts in the database for your context
// with an optional request query parameter of `draft=1`
// returning them as JSON
//
// e.g
// http://localhost:3000/rest/posts
// http://localhost:3000/rest/posts?draft=1
//
app.get('/rest/posts', async (req, res) => {
const context = await commonContext.withRequest(req, res)
// if (!context.session) return res.status(401).end()

const isDraft = req.query?.draft === '1'
const tasks = await context.query.Post.findMany({
where: {
draft: {
equals: isDraft
},
},
query: `
id
title
content
`,
})

res.json(tasks)
})
},

extendHttpServer: (server, commonContext) => {
// e.g
// http://localhost:3000/rest/posts/clu7x6ch90002a89s6l63bjb5
//
server.on('request', async (req, res) => {
if (!req.url?.startsWith('/rest/posts/')) return

// this example HTTP GET handler retrieves a post in the database for your context
// returning it as JSON
const context = await commonContext.withRequest(req, res)
// if (!context.session) return res.status(401).end()

const task = await context.query.Post.findOne({
where: {
id: req.url.slice('/rest/posts/'.length)
},
query: `
id
title
content
draft
`,
})

if (!task) return res.writeHead(404).end()
res.writeHead(200).end(JSON.stringify(task))
})
},
},
lists,
Expand Down
40 changes: 0 additions & 40 deletions examples/extend-express-app/routes/tasks.ts

This file was deleted.

186 changes: 36 additions & 150 deletions examples/extend-express-app/schema.graphql
Original file line number Diff line number Diff line change
@@ -1,37 +1,25 @@
# This file is automatically generated by Keystone, do not modify it manually.
# Modify your Keystone config when you want to change this.

type Task {
type Post {
id: ID!
label: String
priority: TaskPriorityType
isComplete: Boolean
assignedTo: Person
finishBy: DateTime
title: String
content: String
draft: Boolean
}

enum TaskPriorityType {
low
medium
high
}

scalar DateTime @specifiedBy(url: "https://datatracker.ietf.org/doc/html/rfc3339#section-5.6")

input TaskWhereUniqueInput {
input PostWhereUniqueInput {
id: ID
}

input TaskWhereInput {
AND: [TaskWhereInput!]
OR: [TaskWhereInput!]
NOT: [TaskWhereInput!]
input PostWhereInput {
AND: [PostWhereInput!]
OR: [PostWhereInput!]
NOT: [PostWhereInput!]
id: IDFilter
label: StringFilter
priority: TaskPriorityTypeNullableFilter
isComplete: BooleanFilter
assignedTo: PersonWhereInput
finishBy: DateTimeNullableFilter
title: StringFilter
content: StringFilter
draft: BooleanFilter
}

input IDFilter {
Expand Down Expand Up @@ -73,131 +61,38 @@ input NestedStringFilter {
not: NestedStringFilter
}

input TaskPriorityTypeNullableFilter {
equals: TaskPriorityType
in: [TaskPriorityType!]
notIn: [TaskPriorityType!]
not: TaskPriorityTypeNullableFilter
}

input BooleanFilter {
equals: Boolean
not: BooleanFilter
}

input DateTimeNullableFilter {
equals: DateTime
in: [DateTime!]
notIn: [DateTime!]
lt: DateTime
lte: DateTime
gt: DateTime
gte: DateTime
not: DateTimeNullableFilter
}

input TaskOrderByInput {
input PostOrderByInput {
id: OrderDirection
label: OrderDirection
priority: OrderDirection
isComplete: OrderDirection
finishBy: OrderDirection
title: OrderDirection
content: OrderDirection
draft: OrderDirection
}

enum OrderDirection {
asc
desc
}

input TaskUpdateInput {
label: String
priority: TaskPriorityType
isComplete: Boolean
assignedTo: PersonRelateToOneForUpdateInput
finishBy: DateTime
}

input PersonRelateToOneForUpdateInput {
create: PersonCreateInput
connect: PersonWhereUniqueInput
disconnect: Boolean
}

input TaskUpdateArgs {
where: TaskWhereUniqueInput!
data: TaskUpdateInput!
}

input TaskCreateInput {
label: String
priority: TaskPriorityType
isComplete: Boolean
assignedTo: PersonRelateToOneForCreateInput
finishBy: DateTime
}

input PersonRelateToOneForCreateInput {
create: PersonCreateInput
connect: PersonWhereUniqueInput
}

type Person {
id: ID!
name: String
tasks(where: TaskWhereInput! = {}, orderBy: [TaskOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: TaskWhereUniqueInput): [Task!]
tasksCount(where: TaskWhereInput! = {}): Int
}

input PersonWhereUniqueInput {
id: ID
name: String
}

input PersonWhereInput {
AND: [PersonWhereInput!]
OR: [PersonWhereInput!]
NOT: [PersonWhereInput!]
id: IDFilter
name: StringFilter
tasks: TaskManyRelationFilter
}

input TaskManyRelationFilter {
every: TaskWhereInput
some: TaskWhereInput
none: TaskWhereInput
}

input PersonOrderByInput {
id: OrderDirection
name: OrderDirection
}

input PersonUpdateInput {
name: String
tasks: TaskRelateToManyForUpdateInput
}

input TaskRelateToManyForUpdateInput {
disconnect: [TaskWhereUniqueInput!]
set: [TaskWhereUniqueInput!]
create: [TaskCreateInput!]
connect: [TaskWhereUniqueInput!]
}

input PersonUpdateArgs {
where: PersonWhereUniqueInput!
data: PersonUpdateInput!
input PostUpdateInput {
title: String
content: String
draft: Boolean
}

input PersonCreateInput {
name: String
tasks: TaskRelateToManyForCreateInput
input PostUpdateArgs {
where: PostWhereUniqueInput!
data: PostUpdateInput!
}

input TaskRelateToManyForCreateInput {
create: [TaskCreateInput!]
connect: [TaskWhereUniqueInput!]
input PostCreateInput {
title: String
content: String
draft: Boolean
}

"""
Expand All @@ -206,27 +101,18 @@ The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://
scalar JSON @specifiedBy(url: "http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf")

type Mutation {
createTask(data: TaskCreateInput!): Task
createTasks(data: [TaskCreateInput!]!): [Task]
updateTask(where: TaskWhereUniqueInput!, data: TaskUpdateInput!): Task
updateTasks(data: [TaskUpdateArgs!]!): [Task]
deleteTask(where: TaskWhereUniqueInput!): Task
deleteTasks(where: [TaskWhereUniqueInput!]!): [Task]
createPerson(data: PersonCreateInput!): Person
createPeople(data: [PersonCreateInput!]!): [Person]
updatePerson(where: PersonWhereUniqueInput!, data: PersonUpdateInput!): Person
updatePeople(data: [PersonUpdateArgs!]!): [Person]
deletePerson(where: PersonWhereUniqueInput!): Person
deletePeople(where: [PersonWhereUniqueInput!]!): [Person]
createPost(data: PostCreateInput!): Post
createPosts(data: [PostCreateInput!]!): [Post]
updatePost(where: PostWhereUniqueInput!, data: PostUpdateInput!): Post
updatePosts(data: [PostUpdateArgs!]!): [Post]
deletePost(where: PostWhereUniqueInput!): Post
deletePosts(where: [PostWhereUniqueInput!]!): [Post]
}

type Query {
tasks(where: TaskWhereInput! = {}, orderBy: [TaskOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: TaskWhereUniqueInput): [Task!]
task(where: TaskWhereUniqueInput!): Task
tasksCount(where: TaskWhereInput! = {}): Int
people(where: PersonWhereInput! = {}, orderBy: [PersonOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PersonWhereUniqueInput): [Person!]
person(where: PersonWhereUniqueInput!): Person
peopleCount(where: PersonWhereInput! = {}): Int
posts(where: PostWhereInput! = {}, orderBy: [PostOrderByInput!]! = [], take: Int, skip: Int! = 0, cursor: PostWhereUniqueInput): [Post!]
post(where: PostWhereUniqueInput!): Post
postsCount(where: PostWhereInput! = {}): Int
keystone: KeystoneMeta!
}

Expand Down
Loading