Code Monkey home page Code Monkey logo

kotless's Introduction

Kotless Icon Kotless

JetBrains incubator project KotlinLang slack

Kotless stands for Kotlin serverless framework.

Its focus lies in reducing the routine of serverless deployment creation by generating it straight from the code of the application itself.

So, simply speaking, Kotless gives you one magic button to deploy your Web application as a serverless application on AWS and Azure!

Kotless consists of two main parts:

  • DSL provides a way of defining serverless applications. There are three DSLs supported:
    • Kotless DSL — Kotless own DSL that provides annotations to declare routing, scheduled events, etc.
    • Ktor — Ktor engine that is introspected by Kotless. Use standard Ktor syntax and Kotless will generate deployment.
    • Spring Boot — Spring Boot serverless container that is introspected by Kotless. Use standard Spring syntax and Kotless will generate deployment.
  • Kotless Gradle Plugin provides a way of deploying serverless application. For that, it:
    • performs the tasks of generating Terraform code from the application code and, subsequently, deploying it to AWS or Azure;
    • runs application locally, emulates the AWS environment (if necessary) and provides the possibility for IDE debugging.

One of the key features of Kotless is its ability to embed into existing applications. Kotless makes super easy deployment of existing Spring and Ktor applications to AWS and Microsoft Azure serverless platforms.

Getting started

Setting up project

Kotless uses Gradle to wrap around the existing building process and insert the deployment into it.

Consider using one of the latest versions of Gradle, starting with the 7.2 version.

Basically, if you already use Gradle, you only need to do two things.

Firstly, set up the Kotless Gradle plugin.

You will have to tell Gradle where to find the plugin by editing settings.gradle.kts:

pluginManagement {
    resolutionStrategy {
        this.eachPlugin {
            if (requested.id.id == "io.kotless") {
                useModule("io.kotless:gradle:${this.requested.version}")
            }
        }
    }

    repositories {
        maven(url = uri("https://packages.jetbrains.team/maven/p/ktls/maven"))
        gradlePluginPortal()
        mavenCentral()
    }
}

And apply the plugin:

//Imports are necessary, for this example
import io.kotless.plugin.gradle.dsl.Webapp.Route53
import io.kotless.plugin.gradle.dsl.kotless

//Group may be used by Kotless DSL to reduce the number of introspected classes by package
//So, don't forget to set it
group = "org.example"
version = "0.1.0"


plugins {
    //Version of Kotlin should be 1.3.72+
    kotlin("jvm") version "1.5.31" apply true

    id("io.kotless") version "0.2.0" apply true
}

Secondly, add Kotless DSL (or Ktor, or Spring Boot) as a library to your application:

repositories {
    mavenCentral()
    //Kotless repository
    maven(url = uri("https://packages.jetbrains.team/maven/p/ktls/maven"))
}

dependencies {
    implementation("io.kotless", "kotless-lang", "0.2.0")
    implementation("io.kotless", "kotless-lang-aws", "0.2.0")
//    if you want to deploy to Microsoft Azure, just replace -aws with -azure    
//    implementation("io.kotless", "ktor-lang-azure", "0.2.0")


    //or for Ktor (Note, that `ktor-lang` depends on Ktor version 1.5.0)
    //implementation("io.kotless", "ktor-lang", "0.2.0")
    //implementation("io.kotless", "ktor-lang-aws", "0.2.0")
    //implementation("io.kotless", "ktor-lang-azure", "0.2.0")

    //or for Spring Boot (Note, that `spring-boot-lang` depends on Spring Boot version 2.3.0.RELEASE)
    //implementation("io.kotless", "spring-boot-lang", "0.2.0")
    //implementation("io.kotless", "spring-boot-lang-aws", "0.2.0")
    //implementation("io.kotless", "spring-boot-lang-azure", "0.2.0")
}

Please note that if you use Ktor or Spring Boot you will need to replace existing in your project dependency with a special Kotless *-lang dependency. Also, after that you will need to align version of dependent libraries (like Spring Security) with version bundled in *-lang (see this paragraph)

This gives you access to DSL interfaces in your code and sets up a Lambda dispatcher inside your application.

Deploying to the cloud

Depending on a use case, you may want to deploy application either in an AWS or Microsoft Azure.

Note, that if you even don't have a cloud account, you can still use Kotless locally to run and debug your application -- just use local Gradle task.

Deploying to AWS

If you don't have an AWS account, you can create it following simple instruction by Hadi Hariri.

If you have an AWS account and want to perform the real deployment — let's set up everything for it! It's rather simple:

kotless {
    config {

        aws {
            storage {
                bucket = "kotless.s3.example.com"
            }

            profile = "example"
            region = "eu-west-1"
        }
    }

    webapp {
        dns("kotless", "example.com")
    }
}

Here we set up the config of Kotless itself:

  • the bucket, which will be used to store lambdas and configs;
  • Terraform configuration with the name of the profile to access AWS.

Then we set up a specific application to deploy:

  • Route53 alias for the resulting application (you need to pre-create an ACM certificate for the DNS record).

And that's the whole setup!

Deploying to Azure

Deployment to Microsoft Azure is also pretty straightforward and simple:

kotless {
    config {
        azure {
            storage {
                storageAccount = "your-storage-account"
                container = "container-which-kotless-would-use"
            }

            terraform {
                backend {
                    resourceGroup = "your-resource-group"
                }
            }
        }
    }

    webapp {
        dns("kotless", "example.com")
    }
}

Here we set up the config of Kotless itself:

  • the storage, which will be used to store lambdas and configs;
  • Terraform configuration with the name of the profile to access Azure.

Then we set up a specific application to deploy:

  • Azure DNS alias for the resulting application (you need to pre-create certificate for the DNS record).

And that's the whole setup!

Creating application

Now you can create your first serverless application with Kotless DSL:

@Get("/")
fun main() = "Hello world!"

Or with Ktor:

class Server : Kotless() {
    override fun prepare(app: Application) {
        app.routing {
            get("/") {
                call.respondText { "Hello World!" }
            }
        }
    }
}

Or with Spring Boot:

@SpringBootApplication
open class Application : Kotless() {
    override val bootKlass: KClass<*> = this::class
}

@RestController
object Pages {
    @GetMapping("/")
    fun main() = "Hello World!"
}

Local start

Kotless-based applications can start locally as an HTTP server. This functionality is supported by all DSLs.

Moreover, Kotless local start may spin up an AWS emulation (docker required). Just instantiate your AWS service client using override for Kotless local starts:

val client = AmazonDynamoDBClientBuilder.standard().withKotlessLocal(AwsResource.DynamoDB).build()

And enable it in Gradle:

kotless {
    //<...>
    extensions {
        local {
            //enables AWS emulation (disabled by default)
            useAWSEmulation = true
        }
    }
}

During the local run, LocalStack will be started and all clients will be pointed to its endpoint automatically.

Local start functionality does not require any access to cloud provider, so you may check how your application behaves without an AWS account. Also, it gives you the possibility to debug your application locally from your IDE.

Integration with existing applications

Kotless is able to deploy existing Spring Boot or Ktor application to AWS serverless platform. To do it, you'll need to set up a plugin and replace existing dependency with the appropriate Kotless DSL.

For Ktor, you should replace existing engine ( e.g. implementation("io.ktor", "ktor-server-netty", "1.5.0")) with implementation("io.kotless", "ktor-lang", "0.1.6"). Note that this dependency bundles Ktor of version 1.5.0, so you may need to upgrade other Ktor libraries (like ktor-html-builder) to this version.

For Spring Boot you should replace the starter you use ( e.g. implementation("org.springframework.boot", "spring-boot-starter-web", "2.3.0.RELASE)) with implementation("io.kotless", "spring-boot-lang", "0.1.6"). Note that this dependency bundles Spring Boot of version 2.4.2, so you also may need to upgrade other Spring Boot libraries to this version.

Once it is done, you may hit deploy task and make your application serverless. Note, that you will still be able to run application locally via local Gradle task.

Advanced features

While Kotless can be used as a framework for the rapid creation of serverless applications, it has many more features covering different areas of application.

Including, but not limited to:

  • Lambdas auto-warming — Kotless creates schedulers to execute warming sequences to never leave your lambdas cold. As a result, applications under moderate load are not vulnerable to cold-start problem.
  • Permissions management — you can declare which permissions to which AWS resources are required for application via annotations on Kotlin functions, classes or objects. Permissions will be granted automatically.
  • Static resources — Kotless will deploy static resources to S3 and set up CDN for them. It may greatly improve the response time of your application and is supported by all DSLs.
  • Scheduled events — Kotless sets up timers to execute @Scheduled jobs on schedule;
  • Terraform extensions — Kotless-generated code can be extended by custom Terraform code;

Kotless is in active development, so we are currently working on extending this list with such features as:

  • Support of other clouds — Kotless is based on a cloud-agnostic schema, so we are working on support of other clouds.
  • Support of multiplatform applications — Kotless will not use any platform-specific libraries to give you a choice of a Lambda runtime (JVM/JS/Native).
  • Versioned deployment — Kotless will be able to deploy several versions of the application and maintain one of them as active.
  • Implicit permissions granting — Kotless will be able to deduce permissions from AWS SDK function calls.
  • Events handlers support — Kotless will generate events subscriptions for properly annotated events handlers.

Examples

Any explanation becomes much better with a proper example.

In the repository's examples folder, you can find example projects built with Kotless DSL:

  • kotless/site — a site about Kotless written with Kotless DSL (site.kotless.io). This example demonstrates @StaticGet and @Get (static and dynamic routes) usage, as well as Link API.
  • kotless/shortener — a simple URL shortener written with Kotless DSL (short.kotless.io). This example demonstrates @Get ( dynamic routes), @Scheduled (scheduled lambdas), Permissions API (for DynamoDB access), and Terraform extensions.

Similar examples exist for Ktor:

  • ktor/site — a site about Kotless written with Ktor (ktor.site.kotless.io). This example demonstrates static {...} and routing {...} usage.
  • ktor/shortener — a simple URL shortener written with Ktor (ktor.short.kotless.io). This example demonstrates routing { ... } (dynamic routes), Permissions API (for DynamoDB access), and Terraform extensions.

And for Spring Boot:

  • spring/site — a site about Kotless written with Spring Boot (spring.site.kotless.io). This example demonstrates usage of statics and @RestController.
  • spring/shortener — a simple URL shortener written with Spring Boot (spring.short.kotless.io). This example demonstrates usage of @RestController (dynamic routes), Permissions API (for DynamoDB access), and Terraform extensions.

Want to know more?

You may take a look at Wiki where the client documentation on Kotless is located.

Apart from that, the Kotless code itself is widely documented, and you can take a look into its interfaces to get to know Kotless better.

You may ask questions and participate in discussions on #kotless channel in KotlinLang slack.

Acknowledgements

Special thanks to:

  • Alexandra Pavlova (aka sunalex) for our beautiful logo;
  • Yaroslav Golubev for help with documentation;
  • Gregor Billing for help with the Gradle plugin and more.

kotless's People

Contributors

anstkras avatar etolstoy avatar fitzoh avatar kodart avatar martonbtoth avatar n-takehata avatar s-leigh avatar svok avatar tanvd avatar v3rm0n avatar valchukdmitry avatar yrusskih avatar yuppie-flu avatar zaenk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotless's Issues

Java 11 Support

Hello, I tried to deploy a simple function and I initialized my Gradle project using Java 11, which is the latest supported runtime for Java in AWS Lambda.

Everything got deployed correctly, except the AWS lambda runtime was set to java8 and I got this error at runtime:

/HelloserverlessApplication has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0

I'm using kotless 0.1.5 and the spring-boot-lang dsl.

Terraform as code

I think it would be interesting to have in git the generated terraform code.
Now it's difficult, because kotless-gen can clean up with all build directory
If it were in a non-erasable folder, it could be saved to git

Besides, this will allow to change the logic of the deployment to a more interesting option. To deploy you would have had to write the necessary code, generate terraform code and only after that deploy it. This will complicate the deployment a bit, but will allow better control over it and track changes in git

Also, It might be a good idea to move all the generated code to the terraform module so that the user in this folder can also describe its own resources.

The list of supported mime-types is too limited

Now I see the list of supported mime-types:

enum class MimeType(val mimeText: String, val isBinary: Boolean, val extension: String) {
    PLAIN("text/plain", false, "txt"),
    HTML("text/html", false, "html"),
    CSS("text/css", false, "css"),

    PNG("image/png", true, "png"),
    APNG("image/apng", true, "apng"),
    GIF("image/gif", true, "gif"),
    JPEG("image/jpeg", true, "jpeg"),
    BMP("image/bmp", true, "bmp"),
    WEBP("image/webp", true, "webp"),

    JS("application/javascript", false, "js"),
    JSON("application/json", false, "json"),
    XML("application/xml", false, "xml"),
    ZIP("application/zip", true, "zip"),
    GZIP("application/gzip", true, "gzip");
}

And they are too few. In practice many more file types are used including ttf, md, etc. They are hundreds. So, the enum here looks inappropriate.

On Windows 0.1.6 not generates aws_api_gateway_integration and aws_lambda_function resources

Steps to reproduce

Expected outcome

Notes

I did some investigation, will submit a PR with a possible fix shortly. download_terraform task tries to chmod the downloaded terraform executable, which fails on windows.

LocalStack not stopped after local run

Gradle plugin does not request stop of LocalStack after local run.
It does not happen automatically either because local run is performed in the separate process.

Using kotlin("js") plugin with Kotless has conflicting tasks

Attempting to put together a Kotless services that will serve a react app. The application of the plugins have conflicting tasks that they both seem to add if I add the kotlin.js plugin first it conflicts on:

Cannot add task 'processResources' as a task with that name already exists.

If I have Kotless first:

Cannot add task 'testClasses' as a task with that name already exists.

plugins {
    kotlin("js") version "1.3.70" apply true
    id("io.kotless") version "0.1.3" apply true
}

repositories {
    maven("https://kotlin.bintray.com/kotlin-js-wrappers")
    mavenCentral()
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib-js"))

    implementation("io.kotless", "ktor-lang", "0.1.3")

    implementation("org.jetbrains:kotlin-react:16.9.0-pre.89-kotlin-1.3.60")
    implementation("org.jetbrains:kotlin-react-dom:16.9.0-pre.89-kotlin-1.3.60")
    implementation(npm("react", "16.12.0"))
    implementation(npm("react-dom", "16.12.0"))

    implementation("org.jetbrains:kotlin-styled:1.0.0-pre.90-kotlin-1.3.61")
}

Gradle configuration fails because of implicit dependency on ShadowJar

No template, so I will just freestyle the issue report to the best of my abilities!

OS: Manjaro Linux 18.1.4 / Kernel 5.4.2 amd64
Java: 1.8.0_232
Gradle: 6.0.1 (wrapper via gradlew)

Expected behaviour

When following the steps on the Quickstart guide in the README.md file, I can incorporate the Gradle plugin id("io.kotless") into my existing build without breaking the project.

Actual behaviour

Upon following the instructions on how to include Kotless in my own Gradle project, Gradle fails during configuration stage with the following error message:

FAILURE: Build failed with an exception.

* Where:
Build file '/home/suushie_maniac/jvdocs/tnoodle/webscrambles/build.gradle.kts' line: 27

* What went wrong:
An exception occurred applying plugin request [id: 'io.kotless', version: '0.1.2']
> Failed to apply plugin [id 'io.kotless']
   > Task with name 'shadowJar' not found in project ':webscrambles'.

The failure occurs even for simple commands such as ./gradlew tasks.

In particular, my buildscript file uses the Kotlin DSL. I have installed Kotless as id("io.kotless").version("0.1.2") as determined by the Quickstart guide. The plugins {} block also includes johnrengelman's shadow plugin indenpendently of Kotless, because it is needed as part of my project.

Steps to reproduce

The entire codebase of the project where this fail occurs is open-source at https://github.com/suushiemaniac/tnoodle/tree/feature/kotless.

To reproduce, simply clone and check out the feature/kotless branch that is linked above and execute ./gradlew tasks in the project directory.

The relevant build file is in the cloudscrambles folder, found at cloudscrambles/build.gradle.kts

Support merging multiple projects in the same API Gateway

Let's say I have 2 teams. Team 1 works on HelloWorld project and Team 2 works on HiWorld project.Both the teams define their APIs:

  • /helloworld
  • /hiworld

And now I want to deploy both to an API gateway, as we're serving these apis do an external client. I want both to be indexed under api.hi , so

This path is configured in API Gateway as a Custom Domain.

Each API is defined in its own Kotless application, as both teams are doing entire different applications.

I'm not able to do this routing as per API Gateway specification (unless I change how the pathing works). This would be very helpful on using Kotest with a more distributed development

Deploy to aws error

`1:14:17 p.m.: Executing task 'deploy'...

Task :download_terraform
Downloading terraform version 0.11.14 for OS darwin_amd64
Terraform version 0.11.14 for OS darwin_amd64 successfully downloaded

Task :generate
Task :compileKotlin
Task :compileJava NO-SOURCE
Task :processResources NO-SOURCE
Task :classes UP-TO-DATE
Task :shadowJar
There are some problems with the configuration, described below.

The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.

Error: Error loading /Users/malcolmjrosse/Desktop/Kotlin-Devs/UpApp-Server/build/kotless-gen/UpApp-Server.tf: Error reading config for aws_cloudwatch_event_target[autowarm_{name}_get]: parse error at 1:38: expected "}" but found invalid sequence "{"

Task :initialize

Error: Error loading /Users/malcolmjrosse/Desktop/Kotlin-Devs/UpApp-Server/build/kotless-gen/UpApp-Server.tf: Error reading config for aws_cloudwatch_event_target[autowarm_{name}_get]: parse error at 1:38: expected "}" but found invalid sequence "{"

Task :deploy`

Add configuration for AWS Cloudwatch

Configurations such as:

  • Log retention period

  • Log group custom name - Update: This is not possible since AWS lambda expects specific name for the log group.

Gradle build is successful even if deploy fails

I'm using deploy task to release my app to production on Gitlab CI, but the pipeline is successful even if the deploy fails because when terrafrom apply fails it doesn't mark the task as failed.

Different additional Terraform scripts for local/remote deployment

I need access to SSM so I added a policy to the Lambda execution role, but the same role is not created when I'm running it locally so the additional configuration fails.
So either a possibility to have different additional configurations for local/remote or a way to detect in Terraform scripts if it is running locally. Maybe with a variable 🤔

MergeLambda.All creates a function called "merged-0"

By default the deployment uses MergeLambda.All and when there are more than one endpoint, it generates a function called merged-0 which says nothing about the actual application I'm deploying. The name should really be the application name.

For example when using MergeLambda.None the following functions and names are created:

  • com-example-helloserverless-hello-serverless-tasks-get
  • com-example-helloserverless-hello-serverless-overview-get
  • ...

Ideally when using MergeLambda.All the name of the function would be something like

  • com-example-helloserverless

Using kotless 0.1.5 and spring-boot-lang 0.1.5

Authentication documentation

I am using the Ktor interface for Kotless; I was wondering about how to handle authentication; say through JWT tokens, or even connecting up with Auth0. I don't see any documentation about this--I assume Kotless will not inspect Ktor's authentication middleware, but I'm not sure either way. There isn't any documentation regarding adding on API gateway lambda authorizers, either, though I suppose that could be done in the Terraform extension points & by defining the authorizer with the rest of the API.

Any pointers about this would be great; I think this is probably a common concern so it may fit well in the README/Wiki as well. Thanks; this is an amazing toolkit and has worked flawlessly thus far.

KTOR static folders are not recursively included to S3 bucket

This is due to the following code:

                            "io.ktor.http.content.files" -> {
                                val folder = File(base, element.getArgument("folder", binding).asString(binding))

                                val allFiles = folder.listFiles() ?: emptyArray()

                                for (file in allFiles) {
                                    val remotePath = file.toRelativeString(folder).toURIPath()
                                    val path = URIPath(outer, remotePath)

                                    createResource(file, path, context)
                                }
                            }

if I have such a file structure:

fold1
   fold2
      file1
      file2
   fold3
      file3
      file4

and use

static("fold1") {
    files(fold1)
}

I want all the file tree to be included into S3 bucket including file1...file4

Environment variables

Add the ability to use env variable for lambda
It is desirable with the ability to choose different values of variables in each region

[BUG] Scheduled Event Lambda Handler

Currently in https://github.com/JetBrains/kotless/blob/master/dsl/kotless/lang/src/main/kotlin/io/kotless/dsl/LambdaHandler.kt#L43 it uses a raw JSON String check to determine the type of CloudWatch Event. However for example if I created a POST request with body

{
    "Scheduled Event": "Scheduled Event"
}

This will try to deserialize as CW event

val event = Json.parse(CloudWatch.serializer(), jsonRequest)

And then throw

2020-08-18 12:45:37.138 ERROR LambdaHandler:57 - Error occurred during handle of request and was not caught
kotlinx.serialization.MissingFieldException: Field 'source' is required, but it was missing
	at io.kotless.dsl.model.CloudWatch.<init>(CloudWatch.kt)
	at io.kotless.dsl.model.CloudWatch$$serializer.deserialize(CloudWatch.kt)
	at io.kotless.dsl.model.CloudWatch$$serializer.deserialize(CloudWatch.kt:7)
	at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:34)
	at kotlinx.serialization.json.internal.StreamingJsonInput.decodeSerializableValue(StreamingJsonInput.kt:33)
	at kotlinx.serialization.CoreKt.decode(Core.kt:80)
	at kotlinx.serialization.json.Json.parse(Json.kt:126)
	at io.kotless.dsl.LambdaHandler.handleRequest(LambdaHandler.kt:66)
	at io.kotless.local.handler.DynamicHandler.handle(DynamicHandler.kt:45)

Perhaps at least wrap the deserialization? So it can continue to process the HTTP Event?

Document implicit permissions needed for 'kotless user'

I am following along the (instructions)[https://hadihariri.com/2020/05/12/from-zero-to-lamda-with-kotless/] by Hadi Hariri as mentioned in the getting started section of the project's README.md
I think for simplicity's sake he is granting admin access to the user for kotless, but I want to do a more fine tuned setup.
I do not have any experience with AWS so sorry if this is an impossible ask, but essentially what I'm looking for is something like:

  • @Get("/hello") fun helloMsg() = "Hello World!" endpoint needs AWSCodeDeployRoleForLambda
  • static files usage also needs AmazonS3FullAccess
  • DynamoDB usage also needs AmazonDynamoDBFullAccess

Unless I'm wrong about the permissions management feature and its doing more than just declaring what's needed in an app? The way I understand it is that the 'kotless user' which does the deployment of code and all the wiring has different set of permissions than the app itself.

Scanner FieldAnnotationsScanner was not configured

Looks like in the new version of Kotless a bug has been introduced in relation with the reflection capabilities.

With a simple code like:

import io.kotless.dsl.lang.http.Get

@Get("/hello")
fun sayHello() = "Say Hello!"

When the endpoint is hit the following exception is thrown:

org.reflections.ReflectionsException: Scanner FieldAnnotationsScanner was not configured
        at org.reflections.Store.get(Store.java:39)
        at org.reflections.Store.get(Store.java:61)
        at org.reflections.Store.get(Store.java:46)
        at org.reflections.Reflections.getFieldsAnnotatedWith(Reflections.java:546)
        at io.kotless.dsl.reflection.ReflectionScanner.fieldsWithAnnotation(ReflectionScanner.kt:43)
        at io.kotless.local.handler.StaticHandler$fields$2.invoke(StaticHandler.kt:38)
        at io.kotless.local.handler.StaticHandler$fields$2.invoke(StaticHandler.kt:14)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at io.kotless.local.handler.StaticHandler.getFields(StaticHandler.kt)
        at io.kotless.local.handler.StaticHandler.handle(StaticHandler.kt:19)
        at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59)
        at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127)
        at org.eclipse.jetty.server.Server.handle(Server.java:501)
        at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383)
        at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556)
        at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375)
        at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273)
        at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
        at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
        at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:336)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:313)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:171)
        at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:129)
        at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:375)
        at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806)
        at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938)
        at java.base/java.lang.Thread.run(Thread.java:832)

Going back to version 0.1.3 solves the problem (using diverse versions of Kotlin: 1.3.61 and 1.3.72)

Note: The problem is reproducible in local

Could not initialize class io.kotless.dsl.utils.Json

Hello I got this error when doing a post and tring to send a json body, when using serialization plugin
kotlin("plugin.serialization") version "1.3.72"

java.lang.NoSuchMethodError: 'void kotlinx.serialization.internal.SerialClassDescImpl.<init>(java.lang.String, kotlinx.serialization.internal.GeneratedSerializer, int)' at club.themba.quiz.Sample$$serializer.<clinit>(Main.kt:17) at club.themba.quiz.Sample$Companion.serializer(Main.kt) at club.themba.quiz.MainKt.root(Main.kt:24) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106) at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:106) at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:152) at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:110) at io.kotless.dsl.reflection.FunctionCaller.call(FunctionCaller.kt:21) at io.kotless.dsl.app.http.RoutesDispatcher.processRequest(RoutesDispatcher.kt:46) at io.kotless.dsl.app.http.RoutesDispatcher.access$processRequest(RoutesDispatcher.kt:14) at io.kotless.dsl.app.http.RoutesDispatcher$preparePipeline$2.invoke(RoutesDispatcher.kt:35) at io.kotless.dsl.app.http.RoutesDispatcher$preparePipeline$2.invoke(RoutesDispatcher.kt:14) at io.kotless.dsl.app.http.RoutesDispatcher.dispatch(RoutesDispatcher.kt:22) at io.kotless.dsl.LambdaHandler.handleRequest(LambdaHandler.kt:54) at io.kotless.local.handler.DynamicHandler.handle(DynamicHandler.kt:45) at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:500) at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:547) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:270) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938) at java.base/java.lang.Thread.run(Thread.java:834)

reproduce with,

`@Serializable
data class Sample(var hello:String)

@post("/hola", MimeType.JSON)
fun root(): String {
var body = KotlessContext.HTTP.request.myBody?:"""{"hello":"hi"}"""
var bodyJson = parse(Sample.serializer(), body)
return body.hello
}`

Also with out the plugin I can not use the serialization API from kotlin.

Rename `lang` to `kotless-lang`

Current name may lead to problems during export of artifacts. Also, it is not aligned with ktor-lang and spring-boot-lang

Code splitting - one project to n functions

Maybe its already in place but is it possible to generate multiple lambda/function artifacts for a single ktor DSL based project by splitting it per route? Kotless could automatically create and manage API gateway routes pointing to individual lambdas based on the routes defined in the DSL.

Any plans to support cloudformation?

Kotless seems to rely on terraform for infrastructure management. As AWS cloudformation provides a more integrated solution to track resources and their states inside AWS, it would be interesting to know whether there are plans to support cloudformation for example via AWS Cloud development kit?

Add multiregionality

Add the ability to save tf state in one region s3 and push resources in many

This can be done by adding a new list variable, such as aws_regions or something like that

Explain in the readme what really is a serverless deployment creation

From the readme, I learn that Kotless can automatically transform my standard spring boot backend into serverless endpoints.
But I don't really know what is and why use serverless endpoints instead of a normal spring boot .

It seems that each endpoint being a main() make the server actually launch when it is called. So we get the downside of having JVM startup time each time a route is called ?? This seems like a major downside and at what benefit ?
I probably lack knowledge, knowledge that would be great to introduce in the Readme. For now I don't know if Kotless can be useful to me, despite serverless being "en vogue".

Function Default Arguments breake reflection

Example:

@Get("/")
fun root(country: String? = null): String {
    return "4"

Log:

WARN Reflections:396 - could not get type for name org.orangeflag.RootKt.root from any class loader

org.reflections.ReflectionsException: could not get type for name org.orangeflag.RootKt.root
at org.reflections.ReflectionUtils.forName(ReflectionUtils.java:390)
at org.reflections.util.Utils.getMemberFromDescriptor(Utils.java:80)
at org.reflections.util.Utils.getMethodsFromDescriptors(Utils.java:101)
at org.reflections.Reflections.getMethodsAnnotatedWith(Reflections.java:482)
at io.kotless.dsl.reflection.ReflectionScanner.funcsWithAnnotation(ReflectionScanner.kt:26)
at io.kotless.dsl.dispatcher.RoutesCache.scan(RoutesCache.kt:71)
at io.kotless.dsl.Application.init(Application.kt:20)
at io.kotless.dsl.LambdaHandler.handleRequest(LambdaHandler.kt:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at lambdainternal.EventHandlerLoader$StreamMethodRequestHandler.handleRequest(EventHandlerLoader.java:350)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:888)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:293)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:64)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:114)
Caused by: java.lang.ClassNotFoundException: org.orangeflag.RootKt.root
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.reflections.ReflectionUtils.forName(ReflectionUtils.java:388)
... 18 more

ERROR LambdaHandler:63 - Error occurred during handle of request and was not caught

org.reflections.ReflectionsException: Can't resolve member named default for class org.orangeflag.RootKt.root
at org.reflections.util.Utils.getMemberFromDescriptor(Utils.java:94)
at org.reflections.util.Utils.getMethodsFromDescriptors(Utils.java:101)
at org.reflections.Reflections.getMethodsAnnotatedWith(Reflections.java:482)
at io.kotless.dsl.reflection.ReflectionScanner.funcsWithAnnotation(ReflectionScanner.kt:26)
at io.kotless.dsl.dispatcher.RoutesCache.scan(RoutesCache.kt:71)
at io.kotless.dsl.Application.init(Application.kt:20)
at io.kotless.dsl.LambdaHandler.handleRequest(LambdaHandler.kt:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at lambdainternal.EventHandlerLoader$StreamMethodRequestHandler.handleRequest(EventHandlerLoader.java:350)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:888)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:293)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:64)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:114)

Post Request with request body

Using kotless 0.1.5 I do not manage to pass a request body to my request handler, I do not know if this is intended or not(I cannot find a "AtRequestBody" or anything similar), params are passed but not the body which seems to be lost in kotless.dsl.app.http.RoutesDispatcher.kt:42

I created a sample with the minimum code to reproduce here: https://github.com/RemusRD/Kotless-request-body

I follow this steps to reproduce:

./gradlew local

curl --location --request POST 'localhost:8080/greet' \ --header 'Content-Type: application/json' \ --data-raw '{ "name" : "Richard" }'

I get the following stacktrace:

java.lang.IllegalStateException: Required argument 'greetRequest' is missing at io.kotless.dsl.reflection.FunctionCaller.transformToArg(FunctionCaller.kt:35) at io.kotless.dsl.reflection.FunctionCaller.call(FunctionCaller.kt:20) at io.kotless.dsl.app.http.RoutesDispatcher.processRequest(RoutesDispatcher.kt:49) at io.kotless.dsl.app.http.RoutesDispatcher.access$processRequest(RoutesDispatcher.kt:17) at io.kotless.dsl.app.http.RoutesDispatcher$preparePipeline$2.invoke(RoutesDispatcher.kt:38) at io.kotless.dsl.app.http.RoutesDispatcher$preparePipeline$2.invoke(RoutesDispatcher.kt:17) at io.kotless.dsl.app.http.RoutesDispatcher.dispatch(RoutesDispatcher.kt:25) at io.kotless.dsl.LambdaHandler.handleRequest(LambdaHandler.kt:56) at io.kotless.local.handler.DynamicHandler.handle(DynamicHandler.kt:45) at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:59) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:501) at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:383) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:556) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:375) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:273) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103) at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:806) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:938) at java.lang.Thread.run(Thread.java:748)

Can't start local server

No doubt pilot error, but...

When I use
implementation("io.kotless", "lang", "0.1.3")
I get
Error: Could not find or load main class io.kotless.local.MainKt

Changing to
implementation("io.kotless", "lang-local", "0.1.3")
I get
Exception in thread "main" java.lang.IllegalStateException: System.getenv(Constants.Local.serverPort) must not be null
at io.kotless.local.MainKt.main(Main.kt:6)
at io.kotless.local.MainKt.main(Main.kt)

This is all with a default port number.

Thanks for any help.

Custom runtimes with Kotlin/Native

While Ktor doesn't support Kotlin/Native yet maybe the Kotless DSL can be ported. I do believe it will reduce cold boot times and costs, maybe even functions will run faster. Maybe I can give it a shot in my spare time but first I'd like to hear your thoughts. Are you interested in support for custom runtimes in Kotless? Would you consider merging such a pull request from the community? If so I would love to hear about the preferred approach to tackle this issue.

https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html
https://cloud.google.com/appengine/docs/flexible/custom-runtimes/about-custom-runtimes This was a link for App Engine. No support from Cloud Functions yet.
https://docs.microsoft.com/en-us/azure/azure-functions/functions-custom-handlers
https://www.alibabacloud.com/help/doc-detail/132044.htm
https://cloud.ibm.com/docs/openwhisk?topic=openwhisk-runtimes#openwhisk_ref_docker

Kotless doesn't work with ktor-client

When adding ktor client into the project:

implementation("io.ktor:ktor-client-cio:1.3.2")

It shows this error when running:

java.lang.NoSuchMethodError: io.ktor.http.content.OutgoingContent$WriteChannelContent.writeTo(Lkotlinx/coroutines/io/ByteWriteChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
	at io.ktor.server.engine.BaseApplicationResponse$respondWriteChannelContent$$inlined$use$lambda$1.invokeSuspend(BaseApplicationResponse.kt:154)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

I only tested locally though. I'm not sure if it affects actual deployment.

Cannot get property 'kotless' on extra properties extension as it does not exist

plugins {
    kotlin("jvm") version "1.3.50" apply true
    id("io.kotless") version "0.1.2" apply true
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-jdk8"))
    implementation("io.kotless", "lang", "0.1.2")
}

I got this:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring root project 'KotlessDemo'.
> Cannot get property 'kotless' on extra properties extension as it does not exist
IntelliJ IDEA 2019.3 (Ultimate Edition)
Build #IU-193.5233.102, built on November 28, 2019
Subscription is active until December 17, 2019
Runtime version: 11.0.4+10-b520.11 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
Windows 7 6.1
GC: ParNew, ConcurrentMarkSweep
Memory: 984M
Cores: 4

Invalid resource name when using path variable in Spring

What happened?

On a spring REST API Controller like this:

@RestController
class StreamController(final val files: List<FileObject>) {
    @GetMapping("/resource/{id}")
    fun mediaHandler(@PathVariable id: String): ResponseEntity<String> {
        return ResponseEntity.ok().body("Get a specific Foo with id=$id")
    }
}

A kotless deployment will cause this error:

There are some problems with the configuration, described below.

The Terraform configuration must be valid before initialization so that
Terraform can determine which modules and providers need to be installed.

Error: Missing item separator

  on manifestservice.tf line 8, in resource "aws_api_gateway_deployment" "root":
   8:   depends_on = [aws_api_gateway_integration.resource_{id}_get]

Expected a comma to mark the beginning of the next item.


Error: Invalid resource name

  on manifestservice.tf line 19, in resource "aws_api_gateway_integration" "resource_{id}_get":
  19: resource "aws_api_gateway_integration" "resource_{id}_get" {

A name must start with a letter or underscore and may contain only letters,
digits, underscores, and dashes.

Expected behavior

Kotless should generate a valid resource name when using any number of path variables like {id}.

Details

  • Kotlin version: 1.3.72
  • Kotless version: 0.1.6
  • Spring boot versrion: 2.3.3.RELEASE

Kotlin React integration

Is it possible to serve a kotlin react app with kotless?

If so, can you please describe in broad terms what the project setup would look like?
I've checked this jvm-js-fullstack project, but I think this is not the way to go in this case, since we're not able to relay calls to an embedded server just for serving the react app.

Thanks!

Is it possible to use kotliln 1.4 with kotless?

I tried deploying a test project using Kotlin 1.4.0 that otherwise deploys correctly using 1.3.72 but when I tried to deploy I get an error like this:

Unable to find method 'org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(Lorg/jetbrains/kotlin/com/intellij/openapi/project/Project;Ljava/util/Collection;Lorg/jetbrains/kotlin/resolve/BindingTrace;Lorg/jetbrains/kotlin/config/CompilerConfiguration;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lorg/jetbrains/kotlin/com/intellij/psi/search/GlobalSearchScope;ILjava/lang/Object;)Lorg/jetbrains/kotlin/analyzer/AnalysisResult;'.
Possible causes for this unexpected error include:
Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
Re-download dependencies and sync project (requires network)

The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.
Stop Gradle build processes (requires restart)

Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.

In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

Do I need to do something special to get it to work?

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import io.kotless.KotlessConfig.Optimization.MergeLambda
import io.kotless.plugin.gradle.dsl.KotlessConfig.Optimization.Autowarm
import io.kotless.plugin.gradle.dsl.kotless

plugins {
    id("org.springframework.boot") version "2.3.3.RELEASE"
    id("io.spring.dependency-management") version "1.0.10.RELEASE"
    id("io.kotless") version "0.1.6" apply true
    kotlin("jvm") version "1.4.0"
    kotlin("plugin.spring") version "1.4.0"
}

group = "test"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
    //...
}

kotless {
    extensions {
        local {
            port = 8080
        }
        terraform {
            allowDestroy = true
        }
    }

    config {
        bucket = "test-bucket"

        terraform {
            profile = "default"
            region = "us-east-1"
        }
        optimization {
            mergeLambda = MergeLambda.None
            autowarm = Autowarm(false, 0)
        }
    }
}

Feature Request - Replace Autowarm with Provisioned Concurrency

Background

Cold starts and Java functions have always been a bad combo.

A scheduled auto warmer does not fully resolve the issue of cold starts. Concurrent calls will still see cold starts despite the warmer, and may only be helpful for functions that seldom get called. Furthermore having an auto warmer on by default is un-expected and in some instances undesirable.

It would be better to replace the Autowarmer with Provisioned Concurrency which is a lambda feature.

Summary of the feature request

  1. Replace the auto warmer with AWS Lambda Provisioned Concurrency
  2. Allow the user to defined the desired level of concurrency
  3. Provisioned Concurrency should be off by default

Not correct resources for s3

AWS does not support an asterisk to match "all" for s3 resources, so Kotless generation is not correct

kotless(not correct)

statement {
        effect = "Allow"
        resources = ["arn:aws:s3:*:*:kotless.try/*"]
        actions = ["s3:Get*", "s3:List*", "s3:Delete*", "s3:Put*", "s3:Create*"]
    }

correct:

statement {
        effect = "Allow"
        resources = ["arn:aws:s3:::kotless.try/*"]
        actions = ["s3:Get*", "s3:List*", "s3:Delete*", "s3:Put*", "s3:Create*"]
    }

And deploy error:

* aws_iam_role_policy.[...]: Error putting IAM role policy [...]: MalformedPolicyDocument: Resource arn:aws:s3:*:*:kotless.try/* can not contain region information.
status code: 400,

Encoding in HttpResponse

Add possibility to choose encoder for HttpResponse body
For example it maybe class HttpResponse with val body: ByteArray

Custom resources arguments

Need the ability to add resources arguments without using patching for the generated Terraform code

Example:
Want to add minimum_compression_size argument to aws_api_gateway_rest_api resource

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.