Skip to content

Commit

Permalink
enhance example
Browse files Browse the repository at this point in the history
  • Loading branch information
Carmine DiMascio committed Oct 14, 2019
1 parent 50e46f9 commit 2edf681
Show file tree
Hide file tree
Showing 4 changed files with 445 additions and 25 deletions.
179 changes: 179 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# express-openapi-validator-example

Provides a simple example demonstrating how [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) can be used to automatically validate api requests.

## Setup the example project

```shell
# 1. clone this repo
git clone https://github.com/cdimascio/express-openapi-validator-example

# 2. install dependencies
npm install
```

## Run it

Start the Api server

```shell
npm start
```

## Try it

Try the out the following requests.

The [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) automatically validates each request against an [openapi 3 specification](openapi.yaml). If a request is does not match the spec, [express-openapi-validator](https://github.com/cdimascio/express-openapi-validator) automatically returns an appropriate error response.

### Validate a query parameter with a value constraint

```shell
curl -s http://localhost:3000/v1/pets/as |jq
{
"message": "request.params.id should be integer",
"errors": [
{
"path": ".params.id",
"message": "should be integer",
"errorCode": "type.openapi.validation"
}
]
}
```

### Validate a query parameter with a range constraint

```shell
curl -s 'http://localhost:3000/v1/pets?limit=25' |jq
{
"message": "request.query should have required property 'type', request.query.limit should be <= 20",
"errors": [
{
"path": ".query.type",
"message": "should have required property 'type'",
"errorCode": "required.openapi.validation"
},
{
"path": ".query.limit",
"message": "should be <= 20",
"errorCode": "maximum.openapi.validation"
}
]
}
```

### Validate security

```shell
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--data '{}' |jq
{
"message": "'X-API-Key' header required",
"errors": [
{
"path": "/v1/pets",
"message": "'X-API-Key' header required"
}
]
}
```

### Validate content-type

```shell
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/xml' \
--header 'x-api-key: XXXX' \
--data '{
"name": "test"
}' |jq
"message": "unsupported media type application/xml",
"errors": [
{
"path": "/v1/pets",
"message": "unsupported media type application/xml"
}
]
}
```

### Validate a POST request body

```shell
curl -s --request POST \
--url http://localhost:3000/v1/pets \
--header 'content-type: application/json' \
--header 'x-api-key: XXXX' \
--data '{}'|jq
{
"message": "request.body should have required property 'name'",
"errors": [
{
"path": ".body.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
}
]
}
```

### File upload example

```shell
curl -XPOST http://localhost:3000/v1/pets/10/photos -F file=@app.js|jq
{
"files_metadata": [
{
"originalname": "app.js",
"encoding": "7bit",
"mimetype": "application/octet-stream"
}
]
}
```

with the api key and [security handler](https://github.com/cdimascio/express-openapi-validator-example/blob/master/app.js#L24)

```shell
curl -XPOST http://localhost:3000/v1/pets \
--header 'X-Api-Key: XXXXX' \
--header 'content-type: application/json' \
-d '{"name": "spot"}' | jq

{
"id": 4,
"name": "spot"
}
```

### Response validation (optional)

`/v1/pets/99` will return a response that does not match the spec

```
curl -s 'http://localhost:3000/v1/pets/99' |jq
{
"message": ".response should have required property 'name', .response should have required property 'id'",
"errors": [
{
"path": ".response.name",
"message": "should have required property 'name'",
"errorCode": "required.openapi.validation"
},
{
"path": ".response.id",
"message": "should have required property 'id'",
"errorCode": "required.openapi.validation"
}
]
}
```

### ...and much more. Try it out!

## Fetch the spec

curl http://localhost:3000/spec
56 changes: 31 additions & 25 deletions example/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,55 @@ const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const logger = require('morgan');
const http = require('http');
const pets = require('./pets.json');
const OpenApiValidator = require('express-openapi-validator').OpenApiValidator;
const app = express();

// 1. Install bodyParsers for the request types your API will support
app.use(bodyParser.urlencoded());
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.json());

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

const spec = path.join(__dirname, 'openapi.yaml');
app.use('/spec', express.static(spec));

// 1. Install the OpenApiValidator on your express app
const apiSpec = path.join('..', 'test', 'resources', 'openapi.yaml');
// 2. Install the OpenApiValidator on your express app
new OpenApiValidator({
apiSpec,
apiSpec: './example.yaml',
validateResponses: true,
// securityHandlers: {
// ApiKeyAuth: (req, scopes, schema) => true,
// },
}).install(app);

// 2. Add routes
let id = 3;
// 3. Add routes
app.get('/v1/pets', function(req, res, next) {
res.json([{ id: 1, name: 'max' }, { id: 2, name: 'mini' }]);
res.json(pets);
});

app.post('/v1/pets', function(req, res, next) {
res.json({ name: 'sparky' });
res.json({ id: id++, ...req.body });
});

app.get('/v1/pets/:id', function(req, res, next) {
res.json({ id: req.params.id, name: 'sparky' });
const id = req.params.id;
const r = pets.filter(p => p.id === id);
if (id === 99) {
return res.json({ bad_format: 'bad format' });
}
return r.length > 0
? res.json(r)
: res.status(404).json({ message: 'not found', errors: [] });
});

// 2a. Add a route upload file(s)
// 3a. Add a route upload file(s)
app.post('/v1/pets/:id/photos', function(req, res, next) {
// DO something with the file
// files are found in req.files
Expand All @@ -54,23 +70,13 @@ app.post('/v1/pets/:id/photos', function(req, res, next) {
});
});

// 3. Create a custom error handler
// 4. Create a custom error handler
app.use((err, req, res, next) => {
// format error
if (!err.status && !err.errors) {
res.status(500).json({
errors: [
{
message: err.message,
},
],
});
} else {
res.status(err.status).json({
message: err.message,
errors: err.errors,
});
}
// format errors
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});

const server = http.createServer(app);
Expand Down
Loading

0 comments on commit 2edf681

Please sign in to comment.