Laravel Json Api
This package was created to consume an API based on the JSON:API spec made in Laravel with the help of the cloudcreativity/laravel-json-api package.
Features
- Schemas
- Custom headers (Accept, Content-Type, Authorization, etc)
- JSON:API Formatter
- Http requests (GET, POST, PATCH, DELETE, PUT)
- Filter resources
- Get related resources
- Get relationships
- Exception handling
Installation
Install in dart
dart pub add laravel_json_api
Install in flutter
flutter pub add laravel_json_api
Usage
Connecting with our server
import 'package:laravel_json_api/laravel_json_api.dart';
Adapter adapter = ApiController('www.host.com', '/api/v1');
(No need to add http or https protocol)
To use the adapter in our entire application we can wrap it in some state handler, we recommend using provider.
Headers
By default these are the values of the following headers:
Header | Value |
---|---|
Accept | application/vnd.api+json |
Content-Type | application/vnd.api+json |
But they can be overwritten.
import 'package:laravel_json_api/laravel_json_api.dart';
ApiController controller = ApiController('www.host.com', '/api/v1');
print(controller.headers);
controller.addHeader('Authorization', 'Beaer token');
controller.addHeader('Accept', 'application/json');
controller.addHeader('Content-Type', 'application/json');
print(controller.headers);
Adapter adapter = api;
Create Schemas
Schemas provide getters and setters that help us transform responses into objects with their own attributes, relationships, related objects and errors.
For this example we will use a simple blog:
import 'package:laravel_json_api/laravel_json_api.dart';
class Article extends Schema {
//Constructors
Article(ResourceObject resourceObject) : super(resourceObject);
Article.init(String type) : super.init(type);
//Attributes
String get title => getAttribute<String>('title');
set title(String value) => setAttribute<String>('title', value);
String get slug => getAttribute<String>('slug');
set slug(String value) => setAttribute<String>('slug', value);
String get content => getAttribute<String>('content');
set content(String value) => setAttribute<String>('content', value);
String get image => getAttribute<String>('image');
set image(String value) => setAttribute<String>('image', value);
//Relationships
String? get authorId => idFor('user');
set author(User model) => setHasOne('author', model);
Object? get relatedAuthor => includedDoc('users', 'user');
String? get categoryId => idFor('category');
set category(Category model) => setHasOne('category', model);
Object? get relatedCategory => includedDoc('categories', 'category');
}
class Category extends Schema {
//Constructors
Category(ResourceObject resourceObject) : super(resourceObject);
Category.init(String type) : super.init(type);
//Attributes
String get name => getAttribute<String>('name');
set name(String value) => setAttribute<String>('name', value);
String get slug => getAttribute<String>('slug');
set slug(String value) => setAttribute<String>('slug', value);
String get image => getAttribute<String>('image_cover');
set image(String value) => setAttribute<String>('image_cover', value);
//Relationships
Iterable<String> get articlesId => idsFor('articles');
Iterable<Object> get articles => includedDocs('articles');
}
class User extends Schema {
//Constructors
User(ResourceObject resourceObject) : super(resourceObject);
User.init(String type) : super.init(type);
//Attributes
String get firstName => getAttribute<String>('first_name');
set firstName(String value) => setAttribute<String>('first_name', value);
String get email => getAttribute<String>('email');
set email(String value) => setAttribute<String>('email', value);
//Relationships
Iterable<String> get articlesId => idsFor('articles');
Iterable<Object> get articles => includedDocs('articles');
}
All schema methods
String? idFor(String relationshipName)
String? typeFor(String relationshipName)
Map<String, dynamic> dataForHasOne(String relationshipName)
Iterable<dynamic>? dataForHasMany(String relationshipName)
Iterable<String> idsFor(String relationshipName)
Iterable<ResourceObject> includedDocs(String type,[Iterable<String>? ids])
ResourceObject? includedDoc(String type, String relationshipName)
void clearErrorsFor(String attributeName)
bool get hasErrors
bool attributeHasErrors(String attributeName)
Iterable<String> errorsFor(String attributeName)
void clearErrors()
void addErrorFor(String attributeName, String errorMessage)
void setHasOne(String relationshipName, LaravelJsonApiModel model)
Getting data
All of these actions are asynchronous and return a specific value.
Find One
Future<Article> getOneArticle(String id) async {
Article article =
Article(await adapter.find('articles', id) as ResourceObject);
return article;
}
GET | https://www.host.com/api/v1/articles/1
Optionally we can send the forceReload
parameter to cache this resource,
and the queryParam
to sort or include relationships.
Future<Article> getOneArticle(String id) async {
Article article = Article(await adapter.find('articles', id,
forceReload: true,
queryParams: {'include': 'category,user'}) as ResourceObject);
return article;
}
GET | https://www.host.com/api/v1/articles/1?include=category,user
Find All
Future<Iterable<Article>> getAllArticles() async {
Iterable<Article> articles = (await adapter.findAll('articles'))
.map<Article>((article) => Article(article as ResourceObject))
.toList();
return articles;
}
GET | https://www.host.com/api/v1/articles
More getting requests
Future<Iterable<Object>> findManyById(String endpoint, Iterable<String> ids, {Map<String, String> queryParams})
Future<Iterable<Object>> getRelated(String endpoint, String id, String relationshipName)
Future<Iterable<Object>> filter(String endpoint, String filterField, Iterable<String> values, {Map<String, String> queryParams})
Writing data
Article article = Article.init('articles');
article.title = 'Title';
article.content = 'Content';
article.user = user;
article.category = category;
Create resource
Future saveArticle(Article article) async {
await adapter.save('articles', article.jsonApiDoc);
}
Update resource
Article article = Article(await adapter.find('articles', '1') as ResourceObject);
article.title = 'Title Update';
await adapter.save('articles', article.jsonApiDoc);
Replace relationship
Article article = Article(await adapter.find('articles', '1') as ResourceObject);
Category newCategory = Category(await adapter.find('categories', '2') as ResourceObject);
await adapter.replaceRelationship('articles', 'category', article, newCategory);
Delete resource
Article article = Article(await adapter.find('articles', '1') as ResourceObject);
await adapter.delete('articles', article);