Skip to content

Commit

Permalink
start from primsa's testing-express example
Browse files Browse the repository at this point in the history
  • Loading branch information
selimb committed Sep 18, 2022
0 parents commit b9caa30
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 0 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DB_URL="file:./dev.db"
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
dist/
*.env*
!.env.example
!migrations/
170 changes: 170 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# Testing Express

This example shows how to implement integration tests using [Express](https://expressjs.com/), [Supertest](https://github.com/visionmedia/supertest) and [Prisma Client](https://www.prisma.io/docs/concepts/components/prisma-client). It is based on a SQLite database, you can find the database file with some dummy data at [`./prisma/dev.db`](./prisma/dev.db).

## Getting started

### 1. Download example and install dependencies

Download this example:

```
curl https://codeload.github.com/prisma/prisma-examples/tar.gz/latest | tar -xz --strip=2 prisma-examples-latest/typescript/testing-express
```

Install npm dependencies:

```
cd testing-express
npm install
```

<details><summary><strong>Alternative:</strong> Clone the entire repo</summary>

Clone this repository:

```
git clone [email protected]:prisma/prisma-examples.git --depth=1
```

Install npm dependencies:

```
cd prisma-examples/typescript/testing-express
npm install
```

</details>

### 2. Create and seed the database

Run the following command to create your SQLite database file. This also creates the `User` and `Post` tables that are defined in [`prisma/schema.prisma`](./prisma/schema.prisma):

```
npx prisma migrate dev --name init
```

When `npx prisma migrate dev` is executed against a newly created database, seeding is also triggered. The seed file in [`prisma/seed.ts`](./prisma/seed.ts) will be executed and your database will be populated with the sample data.


### 2. Start the REST API server

Rename the `.env.example` to `.env` and execute this command to start the server:

```
npm run dev
```

The server is now running on `http://localhost:3000`. You can send the API requests implemented in `index.js`, e.g. [`http://localhost:3000/feed`](http://localhost:3000/feed).

### 3. Testing the endpoints

The tests are located in the `tests` folder. In these you will find tests handled for cases if a same user is added twice and also to check if the users added are obtained correctly.

The tests can be run using:

```
npm test
```

## Using the REST API

You can access the REST API of the server using the following endpoints:

### `GET`

- `/user`: Fetch all users

### `POST`

- `/user`: Create a new user
- Body:
- `email: String` (required): The email address of the user
- `name: String` (optional): The name of the user


## Switch to another database (e.g. PostgreSQL, MySQL, SQL Server, MongoDB)

If you want to try this example with another database than SQLite, you can adjust the the database connection in [`prisma/schema.prisma`](./prisma/schema.prisma) by reconfiguring the `datasource` block.

Learn more about the different connection configurations in the [docs](https://www.prisma.io/docs/reference/database-reference/connection-urls).

<details><summary>Expand for an overview of example configurations with different databases</summary>

### PostgreSQL

For PostgreSQL, the connection URL has the following structure:

```prisma
datasource db {
provider = "postgresql"
url = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"
}
```

Here is an example connection string with a local PostgreSQL database:

```prisma
datasource db {
provider = "postgresql"
url = "postgresql://janedoe:mypassword@localhost:5432/notesapi?schema=public"
}
```

### MySQL

For MySQL, the connection URL has the following structure:

```prisma
datasource db {
provider = "mysql"
url = "mysql://USER:PASSWORD@HOST:PORT/DATABASE"
}
```

Here is an example connection string with a local MySQL database:

```prisma
datasource db {
provider = "mysql"
url = "mysql://janedoe:mypassword@localhost:3306/notesapi"
}
```

### Microsoft SQL Server

Here is an example connection string with a local Microsoft SQL Server database:

```prisma
datasource db {
provider = "sqlserver"
url = "sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;"
}
```

### MongoDB

Here is an example connection string with a local MongoDB database:

```prisma
datasource db {
provider = "mongodb"
url = "mongodb://USERNAME:PASSWORD@HOST/DATABASE?authSource=admin&retryWrites=true&w=majority"
}
```
Because MongoDB is currently in [Preview](https://www.prisma.io/docs/about/releases#preview), you need to specify the `previewFeatures` on your `generator` block:

```
generator client {
provider = "prisma-client-js"
previewFeatures = ["mongodb"]
}
```
</details>

## Next steps

- Check out the [Prisma docs](https://www.prisma.io/docs)
- Share your feedback in the [`prisma2`](https://prisma.slack.com/messages/CKQTGR6T0/) channel on the [Prisma Slack](https://slack.prisma.io/)
- Create issues and ask questions on [GitHub](https://github.com/prisma/prisma/)
- Watch our biweekly "What's new in Prisma" livestreams on [Youtube](https://www.youtube.com/channel/UCptAHlN1gdwD89tFM3ENb6w)
12 changes: 12 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const path = require('path')
const { defaults } = require('jest-config')

/** @type {import('@jest/types').Config.InitialOptions} */
const config = {
preset: 'ts-jest',
moduleFileExtensions: [...defaults.moduleFileExtensions, 'mjs'],
testEnvironment: path.join(__dirname, 'prisma', 'prisma-test-environment.mjs'),
setupFilesAfterEnv: ['<rootDir>/setupTests.ts'],
}

module.exports = config
36 changes: 36 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "testing-express",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"dev": "ts-node src/index.ts",
"test": "jest"
},
"prettier": {
"semi": false,
"singleQuote": true
},
"dependencies": {
"@prisma/client": "4.3.1",
"@types/node": "16.11.59",
"express": "4.18.1",
"jest-config": "29.0.3"
},
"devDependencies": {
"@types/express": "4.17.14",
"@types/jest": "29.0.3",
"@types/node": "16.11.59",
"@types/supertest": "2.0.12",
"jest": "29.0.3",
"jest-environment-node": "29.0.3",
"nanoid": "4.0.0",
"prisma": "4.3.1",
"supertest": "6.2.4",
"ts-jest": "29.0.1",
"ts-node": "10.9.1",
"typescript": "4.8.3"
},
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}
49 changes: 49 additions & 0 deletions prisma/prisma-test-environment.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @ts-check
import path from 'path'
import fs from 'fs'
import { nanoid } from 'nanoid'
import { TestEnvironment } from 'jest-environment-node'
import { exec } from 'child_process'
import { fileURLToPath } from 'url'

// fix for 'How to fix "__dirname is not defined in ES module scope"'
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename)

const prismaBinary = path.join(
__dirname,
'..',
'node_modules',
'.bin',
'prisma',
)

class PrismaTestEnvironment extends TestEnvironment {
/** @type {import('@jest/types').Config.ProjectConfig} */

constructor(config, _context) {
super(config, _context)

// Generate a unique sqlite identifier for this test context
this.dbName = `test_${nanoid()}.db`
process.env.DB_URL = `file:${this.dbName}`
this.global.process.env.DB_URL = `file:${this.dbName}`
this.dbPath = path.join(__dirname, this.dbName)
}

async setup() {
// Run the migrations to ensure our schema has the required structure
await exec(`${prismaBinary} db push `)
return super.setup()
}

async teardown() {
try {
await fs.promises.unlink(this.dbPath)
} catch (error) {
// doesn't matter as the environment is torn down
}
}
}

export default PrismaTestEnvironment
14 changes: 14 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "sqlite"
url = env("DB_URL")
}

model User {
id Int @default(autoincrement()) @id
name String?
email String @unique
}
39 changes: 39 additions & 0 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { PrismaClient, Prisma } from '@prisma/client'

const prisma = new PrismaClient()

const userData: Prisma.UserCreateInput[] = [
{
name: 'Alice',
email: '[email protected]',
},
{
name: 'Nilu',
email: '[email protected]',
},
{
name: 'Mahmoud',
email: '[email protected]',
},
]

async function main() {
console.log(`Start seeding ...`)
for (const u of userData) {
const user = await prisma.user.create({
data: u,
})
console.log(`Created user with id: ${user.id}`)
}
console.log(`Seeding finished.`)
}

main()
.then(async () => {
await prisma.$disconnect()
})
.catch(async (e) => {
console.error(e)
await prisma.$disconnect()
process.exit(1)
})
1 change: 1 addition & 0 deletions setupTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jest.setTimeout(10000)
29 changes: 29 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { PrismaClient } from '@prisma/client'
import express from 'express'

export const prisma = new PrismaClient()
export const app = express()

app.use(express.json())

app.get(`/user`, async (_req, res) => {
const result = await prisma.user.findMany()
res.json(result)
})

app.post(`/user`, async (req, res) => {
const { name, email } = req.body
try {
const result = await prisma.user.create({
data: {
name,
email,
},
})
res.json(result)
} catch (e) {
res.status(409).json({
error: 'User already exists!',
})
}
})
7 changes: 7 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { app } from './app'

app.listen(3000, () =>
console.log(`
🚀 Server ready at: http://localhost:3000
⭐️ See sample requests: http://pris.ly/e/ts/rest-express#3-using-the-rest-api`),
)
Loading

0 comments on commit b9caa30

Please sign in to comment.