How To Save And Load
Requirements
Save and load requires that your project uses some form of authentication when deployed. This might be done via a plugin such as Auth0 or through an integration with your existing login solution with JSON Web Tokens.
JSON
In order to save a specific design, we convert it to a JSON object, which can be sent to the server and stored in a
database. For this to work, each component must have a function called toJSON
that creates a JSON representation of
its current configuration. Luckily, by extending the STUDIO.BaseComponent
class, we get this for free.
In order to load a specific design, we need to parse the JSON that was saved to the database. To accomplish this, each
component must have a static fromJSON
function. Just like with toJSON
, this is added by extending the
STUDIO.BaseComponent
class.
Handling breaking changes
Let say we have a chair component that comes in red or black.
interface Properties {
color: 'red' | 'black'
}
class Component extends STUDIO.BaseComponent<Properties> {
protected properties: Properties
constructor() {
this.setProperties({ color: 'red' })
}
}
We deploy this project and our customers use it and save their configurations successfully. After a few months, our marketing department has decided that we should no longer produce any black chairs. They have also decided that any chairs that were saved as black should now be orange instead if loaded.
In order to deal with this mess, we implement custom toJSON
and fromJSON
functions.
interface Properties {
color: 'red' | 'orange'
}
class Component extends STUDIO.BaseComponent<Properties> {
protected properties: Properties
constructor() {
this.setProperties({ color: 'red' })
}
toJSON (componentIdMapping: ComponentIdMapping[]): ComponentJSON<Properties> {
return {
v: 2,
ch: this.componentHandler.toJSON(componentIdMapping),
p: this.properties,
}
}
static fromJSON (json: ComponentJSON<Properties>, componentIdMapping: ComponentIdMapping[]) {
const component = new this()
if (json.v === undefined || json.v < 2) {
/**
* Chairs before version 2 which were black should now be orange instead.
* See https://docs.mycompany.com/design-specifications/123
*/
if (json.p.color === 'black') {
json.p.color = 'orange'
}
}
component.properties = json.p
if (json.ch) {
component.componentHandler = ComponentHandler.fromJSON(json.ch, componentIdMapping)
}
return component
}
}
By applying this pattern we can gather all the breaking changes in one place, which improves readability. By catching the change as early as possible when loading the saved design, we can also keep the rest of the code clean from checking values that were valid before but that should no longer be used.
Next time that we have to change something like this in the chair component, we simply increase the version v
to 3
and add antoher if-statement.
toJSON (componentIdMapping: ComponentIdMapping[]): ComponentJSON<Properties> {
return {
v: 3,
ch: this.componentHandler.toJSON(componentIdMapping),
p: this.properties,
}
}
static fromJSON (json: ComponentJSON<Properties>, componentIdMapping: ComponentIdMapping[]) {
const component = new this()
if (json.v === undefined || json.v < 3) {
// Handle versions lower than 3 here
}
if (json.v === undefined || json.v < 2) {
/**
* Chairs before version 2 which were black should now be orange instead.
* See https://docs.mycompany.com/design-specifications/123
*/
if (json.p.color === 'black') {
json.p.color = 'orange'
}
}
component.properties = json.p
if (json.ch) {
component.componentHandler = ComponentHandler.fromJSON(json.ch, componentIdMapping)
}
return component
}