I am deciding between PHP, Python, Node.js and Go for quickly developing RESTful web services.
I am avoiding Java or .NET due to their heavy setup. They usually need heavy IDE and app servers to configure and deploy.
My requirements:
- RESTful web services can be written as a bunch of stateless functions/methods
- Ideally, the framework should allow some sort of password-based authentication and access authorization (using cookie session/token) if possible.
- During development, this web service class can then be quickly re-deployed on a lightweight HTTP server for re-testing with each change. Maybe edit the file directly in www/public_html directory and/or restart the web server.
- If the same framework and server setup can be used for production (security and performance wise) will be great. Else need to retest and re-setup again.
Which of the 4 would be best?
First I do not understand your opinion on Java is heavy setup. Maybe you can been observing a lot of tutorials out there using Eclipse and similar IDE which is heavy stuff themselves. These IDE are built for tons of purposes and not just a coding platform. It would also seems funny to call servlet containers like Apache Tomcat or Eclipse Jetty.
If you are interested to read up more on some tutorial like the following. You will find it is really "easy" to create and test out RESTful API in Java environments.
https://spring.io/guides/gs/rest-service/
Below is a very brief set of instructions to get started (mostly following in the tutorial above)
Prerequisites: Installation of gradle make tool
Code:
$ tree
.
├── build.gradle
└── src
└── main
└── java
└── hello
├── Application.java
├── Greeting.java
└── GreetingController.java
4 directories, 4 files
Code:
#To start
$ gradle bootRun
:compileJava
:processResources UP-TO-DATE
:classes
:findMainClass
:bootRun
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.4.1.RELEASE)
2016-10-19 02:30:03.322 INFO 3687 --- [ main] hello.Application : Starting Application on Enterprise with PID 3687 (/Users/davidktw/SAMPLE/build/classes/main started by davidktw in /Users/davidktw/SAMPLE)
2016-10-19 02:30:03.328 INFO 3687 --- [ main] hello.Application : No active profile set, falling back to default profiles: default
2016-10-19 02:30:03.497 INFO 3687 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@50a638b5: startup date [Wed Oct 19 02:30:03 SGT 2016]; root of context hierarchy
2016-10-19 02:30:05.130 INFO 3687 --- [ main] org.xnio : XNIO version 3.3.6.Final
2016-10-19 02:30:05.159 INFO 3687 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.3.6.Final
2016-10-19 02:30:05.251 WARN 3687 --- [ main] io.undertow.websockets.jsr : UT026009: XNIO worker was not set on WebSocketDeploymentInfo, the default worker will be used
2016-10-19 02:30:05.251 WARN 3687 --- [ main] io.undertow.websockets.jsr : UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
2016-10-19 02:30:05.268 INFO 3687 --- [ main] io.undertow.servlet : Initializing Spring embedded WebApplicationContext
2016-10-19 02:30:05.269 INFO 3687 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1777 ms
2016-10-19 02:30:05.456 INFO 3687 --- [ main] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2016-10-19 02:30:05.483 INFO 3687 --- [ main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2016-10-19 02:30:05.484 INFO 3687 --- [ main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2016-10-19 02:30:05.484 INFO 3687 --- [ main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2016-10-19 02:30:05.485 INFO 3687 --- [ main] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2016-10-19 02:30:05.819 INFO 3687 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@50a638b5: startup date [Wed Oct 19 02:30:03 SGT 2016]; root of context hierarchy
2016-10-19 02:30:05.898 INFO 3687 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting]}" onto public hello.Greeting hello.GreetingController.greeting(java.lang.String)
2016-10-19 02:30:05.902 INFO 3687 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-10-19 02:30:05.903 INFO 3687 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-10-19 02:30:05.940 INFO 3687 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-10-19 02:30:05.941 INFO 3687 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-10-19 02:30:05.993 INFO 3687 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-10-19 02:30:06.235 INFO 3687 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-10-19 02:30:06.318 INFO 3687 --- [ main] b.c.e.u.UndertowEmbeddedServletContainer : Undertow started on port(s) 8080 (http)
2016-10-19 02:30:06.323 INFO 3687 --- [ main] hello.Application : Started Application in 3.86 seconds (JVM running for 4.373)
> Building 80% > :bootRun
# After that, just access using curl
$ curl http://localhost:8080/greeting?name=David
{"id":2,"content":"Hello, David!"}
All the above are done using just my VIM editor. Clearly getting started doesn't need any IDE. The testing environment requires an embedded servlet container, and I configured the spring boot to use the Jboss undertow servlet container (
http://undertow.io)
Point (1) & (2)
If you really want baremetal RESTful API, you can actually be as simple as just writing CGI scripts and place them in the Apache webserver with proper directory structures and scripted using any scripting engine you like. If you want security at the request level, you can delegate it to the job of the servlet container or the web server to secure the API endpoints. If you need access controls, rate limiting and so forth, it will have to go deep into your API design. Or you like, employ the use of a API gateway to do this for you.
Point (3) & (4)
How you deploy your codes and how you maintain them consistently across environments is actually your workflow and processes and coding styles. It is possible to deploy the same piece of package across servlet containers found in different environments and governed by your property files which contain different domains or different databases or different external systems to connect to. It's not really the job of the servlet container or the web server to do this for you.
Actually it seems the so call "easy" that you are searching is very much an idealistic environment. When you start small, everything seems innocent. Then people come in expect easy to migrate, must be secure, must be robust and must be able to adapt to different data layers or environments, must to able to handle high loads. That's where things start to be complex and when it gets complicated, what you previously deem as "difficult" becomes the starting point of "easy".
Below is just a simple "ab" test on the performance of the simple RESTful API.
Code:
$ ab -n 10000 -c 10 http://localhost:8080/greeting
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests
Server Software:
Server Hostname: localhost
Server Port: 8080
Document Path: /greeting
Document Length: 34 bytes
Concurrency Level: 10
Time taken for tests: 7.895 seconds
Complete requests: 10000
Failed requests: 9994
(Connect: 0, Receive: 0, Length: 9994, Exceptions: 0)
Write errors: 0
Total transferred: 1579061 bytes
HTML transferred: 368940 bytes
Requests per second: 1266.56 [#/sec] (mean)
Time per request: 7.895 [ms] (mean)
Time per request: 0.790 [ms] (mean, across all concurrent requests)
Transfer rate: 195.31 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 1.9 2 27
Processing: 1 6 4.5 4 89
Waiting: 0 4 4.2 3 75
Total: 1 8 4.8 6 89
Percentage of the requests served within a certain time (ms)
50% 6
66% 7
75% 8
80% 9
90% 12
95% 17
98% 23
99% 27
100% 89 (longest request)
Anyway, you might also want to take a look at Swagger.io to help in your API building process. Do take a look at Apigee and similar API management service. Some of the features of what you requested actually are part of the management.
I also found
http://www.django-rest-framework.org which you might want to look at.
Personally I find it hard to say which is easier. Because easy is subjective to the environment you are in. If you have systems which you need to integrate with and they have libraries found in certain languages, you end up "easier" working with the language and framework you are less comfortable with because the heavy lifting is resolved. So