Let’s say I have a REST API with a User resource. There are three methods that work with the User resource: POST to create, GET to download and PATCH to change. All methods operate on the same User type, but have different required properties – when creating the user using POST, all request fields except the user ID are mandatory, when downloading using GET, all response fields are mandatory including the ID, when changing using PATCH, all request fields are optional and nullable.
Can I succintly describe this in OpenAPI/Swagger? I would like to describe the User type just once and then only say which fields are required/nullable for each method. A bit like this, in pseudocode:
definitions:
User:
properties:
id:
type: integer
name:
type: string
…
paths:
/users
post:
request:
schema: User
required: [name, …]
nullable: […]
response:
schema: User
required: [id, name, …]
nullable: […]
/users/{id}:
get:
response:
schema: User
required: [id, name, …]
nullable: […]
patch:
request:
schema: User
required: []
nullable: [name, …]
This way I would not have to repeat all the field definitions while having the particular constraints described for each method. Is that possible? Does it make sense?
Related
I am currently working on the implementation of push notifications. To do this, the web push api requires the subscription object which contains the public as well as the private key. Additionally the subscription needs to be stored in a database for later use.
#################
#Subscription Object
#################
endpoint: { type: String, unique: true},
keys: {
p256dh: {type: String},
auth: {type: String}
},
#################
#Sending the Notification
#################
webpush.sendNotification(subscription, payload).catch(console.log);
Personally, I am not sure if I should save the private key in the database.
Is there a better approach to this?
Or is it actually safe to store this information in this format?
I'm generating an API in Stoplight and I'm pretty new at it so I'll describe what I'd like to do in words first.
This is a contrived example but here goes. I have a User object with properties: ID, Name, Password, and an array of EmailAddress objects. The EmailAddress object has the following properties: ID, Address, IsPrimary.
When I create my schema for both User and EmailAddress, I'm leaving the ID fields off. The reason I'm doing this is because in the POST for /users, the ID won't be included for either object type in the request body. So the request for the POST looks like so (responses omitted for now):
post:
summary: ''
operationId: post-users
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/User'
So far so good. But in the response, I'd like to return the full User object with an ID attached. Easy enough, I says (requestBody omitted):
post:
summary: ''
operationId: post-users
responses:
'201':
description: Created
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/User'
- $ref: '#/components/schemas/Id'
(where the Id schema contains just a string property named id)
Where I'm confused is for the EmailAddress objects. I'd like to return those with IDs attached to them but I don't know how to "inject" the IDs in there. I.e. I want to send a User without an ID and with an array of EmailAddress objects also without IDs. But I want to return a User with an ID and with an array of EmailAddress objects with IDs.
Do I need to create a separate UserRequest and UserResponse schema in this case, one with EmailAddress objects with IDs and one without?
Larger question: is returning the entire object in a POST a common thing? I've seen references that say to just return the ID of the new object and others to say return the entire thing to save an extra call to the server to GET it again after the POST completes.
Which Category is your question related to?
DataStore (GraphQL API)
Amplify CLI Version
7.6.23
What AWS Services are you utilizing?
DataStore (GraphQL API)
Provide additional details e.g. code snippets. Be sure to remove any sensitive data.
I am trying to build a service where people can buy subscriptions to certain "Persons" and consume their information. I want to restrict people so that they can only access the data of a certain medium when they are subscribed to it.
Here is the basic structure:
type Post #model {
id: ID!
text: String!
personID: ID! #index(name: "byPerson")
person: Person! #belongsTo(fields: ["personID"])
}
type Person #model {
id: ID!
name: String!
posts: [Post] #hasMany(indexName: "byPerson", fields: ["id"])
}
type Subscription #model {
id: ID!
personID: ID! #index(name: "byPerson")
person: Person! #belongsTo(fields: ["personID"])
userSub: String! // or whatever data we need to reference the user
}
So we have Subscriptions to Persons and Persons can have multiple posts. It is not necessary to fetch a Person if you want to fetch the Posts that a user should be able to see.
What should be possible:
Users should only be able to fetch the posts of the persons that they are subscribed to. There are two ways that I can think of doing but they all require me to change/update data. Since all the data is present, I am not a fan of such solutions.
Solution #1:
Add a group to each user, attach it to the post and add the user to the group as soon as he subscribed
type Post #model #auth(rules: [{ allow: groups, groupsField: "groups" }]) {
id: ID!
text: String!
personID: ID! #index(name: "byPerson")
person: Person! #belongsTo(fields: ["personID"])
groups: String!
}
Not a fan, it requires me to create a group each time a Person is created and I basically have duplicated information here with each post.
Solution #2:
Use an owner field and attach the user as soon as he subscribes
type Post #model #auth(rules: [{ allow: owner, ownerField: "subscribers" }]) {
id: ID!
text: String!
personID: ID! #index(name: "byPerson")
person: Person! #belongsTo(fields: ["personID"])
subscribers: [String]
}
Not a fan as well, I need to edit all the postings as soon as a user subscribes/cancels his subscriptions. The margin of error and amount of calculations here could be huge
I have thought about using a custom resolver (no idea if that works, I don't fully understand it yet) or a custom lambda auth check. The custom lambda auth check causes some issues in the frontend with DataStore. Apparently I need to manually refresh the token for the API or something like that.
What do I want to do?
I would love to use the subscription userSub field as an owner field for the posts. Is that possible (with DataSync) in any way?
I am using OpenAPI 3.0 to describe the schema of the APIs which are running. I have to require two parameters in different locations. What I want to achieve:
parameters:
- oneOf:
- in: cookie
name: username
description: User's name
schema:
type: string
required: true
- in: query
name: nickname
description: User's nickname
schema:
type: string
required: true
I have read
How to require at least one of two parameters in OpenAPI?
How to define mutually exclusive query parameters in Swagger (OpenAPI)?
But those solutions only solve the problems when the parameters are in the same location, so they did not solve my problem.
I have two collections: persons and pets. A person may have as many pets as they want.
Person document:
person {
id: person-id
data: person-info
}
Pet document:
pet {
id: pet-id
data: pet-info
personId: person-id
}
This is my API naming design
GET all pets from a person: /api/pets/:personId
GET all pets with condition: /api/pets/:personId?age_greater_than=4
POST create new pet: /api/pets with the request body that contains person-id
PUT update pet info: /api/pets/:petId with the request body that contains person-id and updated info
DELETE delete pet: /api/pets/:personId with request body that contains pet-ids
Is there something wrong with my API naming convention and how can this be improved? I think that passing person-id directly to /api/pets is kind of weird.
Naming conventions can be found here: https://restfulapi.net/resource-naming/
Please also check again HTTP methods and REST.
Basic concept is that URLs represent resources and HTTP methods you apply to those URLs indicate what you want to do with those resources.
GET - Read resource(s)
PUT/POST - Create resource(s)
PATCH - Update resource(s)
DELTE - Delete resource(s)
So maybe you could use PATCH instead of PUT for updating resources if you only change a part of the resource's attributes.
Also in the DELETE example you should use the pet id as path variable and not person id.
For getting all pets of a person, I think /api/person/id/pets could be more straightforward as /api/pets/personid because when you see the URL /api/pets/23 you don't know if it is pet 23 or all pets of person with id 23.
Think about the best practices again (just use a search engine of your choice) ;)